Thursday, August 16, 2012

Using nwam to set a static ip on Open Indiana


Using Networking Automagic (NWAM) under Open Indiana is sweet.  It puts all those annoying Solaris network configuration elements into one sub-system, and then adds value by allowing us to have multiple network configurations and switch between them.

In NWAM parlance, an ncp is a Network Configuration Profile where you set the ip address, subnet etc and you can have as many of these as you like.  In order for DNS to work, you also have one or more locations (called a LOC) in which we set the search domain, dns servers etc and you can switch between them too.

When we needed to deploy a server for a remote office we simply created a ncp and a loc for that office, and switched to it just before turning the machine off.  This is how we did that.

First, determine the name of the network interface.  In this case nge0
$ ifconfig -a
lo0: flags=2001000849 mtu 8232 index 1
        inet 127.0.0.1 netmask ff000000
nge0: flags=1000843 mtu 1500 index 5
        inet 10.10.1.44 netmask ffffff00 broadcast 10.10.1.255
Then configure a ncp for your physical network , and also a loc for the dns settings;
$ sudo /usr/sbin/nwamcfg
nwamcfg> create ncp 132DaveySt
nwamcfg:ncp:132DaveySt> create ncu phys nge0
Created ncu 'nge0'.  Walking properties ...
activation-mode (manual) [manual|prioritized]> prioritized 
enabled (true) [true|false]>
priority-group> 0
priority-mode [exclusive|shared|all]> shared
link-mac-addr>
link-autopush>
link-mtu>
nwamcfg:ncp:132DaveySt:ncu:nge0> end
Committed changes
nwamcfg:ncp:132DaveySt> create ncu ip nge0
Created ncu 'nge0'.  Walking properties ...
enabled (true) [true|false]>
ip-version (ipv4,ipv6) [ipv4|ipv6]>
ipv4-addrsrc (dhcp) [dhcp|static]> static
ipv4-addr> 10.10.1.44/24
ipv4-default-route> 10.10.1.254
ipv6-addrsrc (dhcp,autoconf) [dhcp|autoconf|static]> dhcp,autoconf,static
ipv6-addr>
ipv6-default-route>
nwamcfg:ncp:132DaveySt:ncu:nge0> end
Committed changes
nwamcfg:ncp:132DaveySt> end
nwamcfg> list
NCPs:
132DaveySt
Automatic
Locations:
Automatic
NoNet
User
nwamcfg> create loc 132DaveySt
Created loc '132DaveySt'. Walking properties ...
activation-mode (manual) [manual|conditional-any|conditional-all]>
enabled (false) [true|false]> true
nameservices (dns) [dns|files|nis|ldap]> dns
nameservices-config-file ("/etc/nsswitch.dns")>
dns-nameservice-configsrc (dhcp) [manual|dhcp]> manual
dns-nameservice-domain> natoffice.wilderness.org.au
dns-nameservice-servers> 10.10.1.6
dns-nameservice-search> natoffice.wilderness.org.au
nfsv4-domain>
ipfilter-config-file>
ipfilter-v6-config-file>
ipnat-config-file>
ippool-config-file>
ike-config-file>
ipsecpolicy-config-file>
nwamcfg:loc:132DaveySt> end
Committed changes
nwamcfg> end
and then we enable the NCP and the LOC and we're good to go.
$ sudo /usr/sbin/nwamadm enable -p ncp 132DaveySt
$ sudo /usr/sbin/nwamadm enable -p loc 132DaveySt

Wednesday, August 08, 2012

I made it to Hackaday! :) :)

It's probably shameless cross promotion, but having been a long time reader of HACKADAY, it's been one of my secret ambitions to be posted up there, along with the other tech-hackers-nerd-makers-whatever-you-wanna-callits. And with TINY, i made it. Hooray for me :)


Look, it's probably a bit self indulgent of me, but i just want to, you-know, hand out a bit of life advice so listen up; people! if you want to do something, then just go out there and do it! All you have to do is trust your instincts and follow your dreams and you too can create arcane nerd oddities and be celebrated for it! :) Thankyou Internet; you made my day today.  That's all, I'll stop now.

Monday, August 06, 2012

Logging multiple sensors to Cosm with Arduino

I'm no rocket scientist (congratulations NASA btw), but when I started out with my EtherTen (ethernet enabled arduino) i was amazed how easy it was to get internet data logging happening.

 

I first started with the example sketch in the Arduino IDE, but i fell down when i discovered that the example code only supports one sensor, and I wanted two (indoor and outdoor temp sensors).  Here's what i did to get two sensors running:
  1. Connected two thermistors using 10k pad resistors to my Arduino pins A0 and A1 as per the example.
  2. I created an account with cosm and had it generate me an api key etc.  Despite looking quite lovely, that site seems a little unintuitive, but stick with it; it will make sense after awhile.
  3. Create a new Arduino device/feed and copy the example sketch it provides you.  Note your API key and FEEDID.
  4. Create a new sketch in the Arduino IDE but instead of using their example (which will work fine for one sensor) use the sketch below.
  5. Substitute in your API key and FEEDID.  
  6. Change IPAddress ip(10,0,1,20); to suit you network.  You can use dhcp by simply following the layout from the example CosmClient sketch in the IDE.
  7. Finally change the name of your sensors from Inside, Outside, to something sane for you.
  8. Compile the code, upload to the Arduino etc and you should be good to go.
I have not yet however managed to get cosm to allow me to stack two data ranges on the same chart.

Sunday, August 05, 2012

Interfacing Atari 2600 Joystick to Arduino

Cleaning up the study the other day i found an old joystick from an Atari 2600.  So like everything else, I tried to connect it to my Arduino :)

The Arduino "button" example uses pull-down resistors, but after awhile i realized that approach wasn't going to work. This joystick switches to ground, meaning that any voltage going to say the Fire button will "dissapear" when the button is pressed. Same for each of the directions too, because this is a "digital" joystick, they're all just buttons inside the joystick.

So the answer is to measure what's going *into* the joystick, not what's coming out. For each of the joystick pins we send 5v into the joystick, and monitor that voltage. If there's 5v going in, then nothing has happened. But if that suddenly drops to near nothing, then a button has been pressed.

Figure 1: Current limiting resistors.

/*
 Interfacing Atari Joystick to Arduino:
 
 Returns output of Atari 2600 9 pin joystick to Arduino serial console.
 
       +---------> Right
       | +-------> Left
       | | +-----> Down
       | | | +---> Up 
       | | | | 
   _____________
 5 \ x o o o o / 1
    \ x o x o / 
   9 `~~~~~~~' 6
        |   |
        |   +----> Button
        +--------> Ground
 
 pinout via http://www.epanorama.net/documents/joystick/ataristick.html
 
 The circuit:
 * 5v through 10k resistor to each of the 5 joystick output pins
 * Arduino ground to Joystick ground
 * 5 data wires from joystick inputs to the arduino pins in the format:
    5V -> 10K resistor > Data to Arduino > Joystick Input
    
 created 2012 by Julius Roberts http://hooliowobbits.blogspot.com.au/
 
 This example code is in the public domain.
 */

// constants won't change. They're used here to set pin numbers:
const int FireButtonPin = 2;     
const int JoystickUpPin = 3;
const int JoystickDownPin = 4;
const int JoystickLeftPin = 5;
const int JoystickRightPin = 6;

void setup() {
  // initialize the Joystick pins as inputs:
  pinMode(FireButtonPin, INPUT);     
  pinMode(JoystickUpPin, INPUT);
  pinMode(JoystickDownPin, INPUT);
  pinMode(JoystickLeftPin, INPUT);
  pinMode(JoystickRightPin, INPUT);

  Serial.begin(9600);
}

void loop(){
  // read the state of the pushbutton value.  Because we're using a
  // pullup resistor, the value for each of the variables will normally 
  // be 1, and 0 when pressed.  This is the opposite of normal 
  // programming logic where 1 = true.  So we invert the variable using
  //  the map function  
  int FireButtonState = digitalRead(FireButtonPin); 
  FireButtonState = map(FireButtonState,0,1,1,0); 
  int JoystickUpState = digitalRead(JoystickUpPin); 
  JoystickUpState = map(JoystickUpState,0,1,1,0);
  int JoystickDownState = digitalRead(JoystickDownPin); 
  JoystickDownState = map(JoystickDownState,0,1,1,0);
  int JoystickLeftState = digitalRead(JoystickLeftPin); 
  JoystickLeftState = map(JoystickLeftState,0,1,1,0);
  int JoystickRightState = digitalRead(JoystickRightPin);  
  JoystickRightState = map(JoystickRightState,0,1,1,0);

  // output the setting to the serial console
  Serial.print("Fire: "); Serial.print(FireButtonState);
  Serial.print(" Up: "); Serial.print(JoystickUpState);
  Serial.print(" Down: "); Serial.print(JoystickDownState);
  Serial.print(" Left: "); Serial.print(JoystickLeftState);
  Serial.print(" Right: "); Serial.println(JoystickRightState);
}

Friday, August 03, 2012

Managing many LEDs on Arduino

Arguably the most simple Arduino project of all is the "blink" sketch which comes pre-installed the Arduino software (examples > basics > blink).  Some Arduino boards even have a surface mount LED already connected to pin 13 so you can do this sketch without any additional hardware.

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.
 
  This example code is in the public domain.
 */
 
// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);     
}

// the loop routine runs over and over again forever:
void loop() {
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);               // wait for a second
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);               // wait for a second
}

And once you get that far, it's not much more difficult to blink multiple lights in some sequence, using a separate arduino pin for each LED you wish to light, and some logic defining which to blink when, or which delay etc.  The "problem" is that as you add more LEDs to such a project each LED requires a dedicated pin. 10 LEDs = 10 pins, 15 LEDs = 15 pins etc, so that approach reasonably scales up to about 16 pins, give or take.  After that you need a smarter way to address the LEDs.  One way is to buy a fancy LED array or the alternative is to make your own.  I chose the latter of course :)

The video below to shows how to wire a circuit in order to allow your Arduino to drive more LEDs than you have pins, and also how to use a technique called multiplexing to avoid some of the compromises in not actually having one pin per LED.  I use this method to wire the circuit board in TINY (TINY Is Not space invaYders) which features a 5x5 array of 25 LEDs using "only" 10 pins.


TINY (This Is Not space invaYders)

Or "Yet Another Arduino Blinken Light Thing" :)

So we were chatting on the TASLUG (Tasmanian Linux Users Group) mailing list talking about a new hackerspace development and whatnot when one of the fellows said he hoped that it didn't become a club of Arduino fanboys.  I said what's up with Arduino, it's awesome, and he said there's far too much boring blinkenlights and not enough actual cool stuff.  It was a bit tongue in cheek, but regardless, my nerd pride was offended and I managed to interpret it as a challenge :)   A month or so later and I had created TINY:


I'm pretty sure nerd etiquette precludes me from proclaiming success, but it did have an awesome time doing it.  I am curious though, what do you think? ;)
/*
  Space Invaders
  Turns on an LED on for one second, then off for one second, repeatedly.
 
  This example code is in the public domain.
 */
 
// pin assignments
int annodes0 = 2;int annodes1 = 3;int annodes2 = 4;int annodes3 = 5;int annodes4 = 6;
//pin 7 is spare
int cathodes0 = 8;int cathodes1 = 9;int cathodes2 = 10;int cathodes3 = 11;int cathodes4 = 12;
int IndicatorLED = 13;
int InputPot = A0;
int C_ButtonPin = 18; //ie analog4, in use as a digital pin
int C_SpeakerPin = 19; //ie analog5, in use as a digital pin

int mycursor=2;  // set initial position of the cursor
//int difficulty = 1; // where 1 is easy and 3 is hard.

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the pins
  pinMode(annodes0, OUTPUT); pinMode(annodes1, OUTPUT); pinMode(annodes2, OUTPUT); pinMode(annodes3, OUTPUT); pinMode(annodes4, OUTPUT);
  pinMode(cathodes0, OUTPUT); pinMode(cathodes1, OUTPUT); pinMode(cathodes2, OUTPUT); pinMode(cathodes3, OUTPUT); pinMode(cathodes4, OUTPUT);
  pinMode(IndicatorLED, OUTPUT); 
  pinMode(C_ButtonPin, INPUT);  pinMode(InputPot, INPUT);
 
  Serial.begin(9600);
}

void loop() {
   //basicMultiplexing(); 
   //SoundEffectGenViaPot();  
   //MakeASmiley();   
   //lightled(6,2000); delay(2000);
   //int buttonState = digitalRead(C_ButtonPin); if (buttonState == HIGH) { sound(7); }  
   
   int difficulty = introSequence(); delay(200);
   SnotVaders(difficulty);     delay(200);
   MakeASmiley();   delay(1000);
}

int introSequence(){
   int difficultyX = SimpleVelocityLoop();
   //int difficulty = map(difficultyX, 0, 1023, 3, 1); 
   int difficulty = 0;
   if (difficultyX < 315) { difficulty = 3; }
   else if (difficultyX > 648 ) { difficulty = 1; }
   else { difficulty = 2; }
      
   Serial.print("difficultyX = "); Serial.println(difficultyX); 
   Serial.print("difficulty = "); Serial.println(difficulty); 

   delay(200);
   int mydelay=10;
   for (int i = 1; i <= 25; i++) {
      sound(1);
      lightled(i,mydelay); delay(mydelay);       
      lightled(25-i,mydelay); delay(mydelay);  
   }
   sound(7);  
   delay(400);
   return difficulty;
}

void SnotVaders(int difficulty){
   // (number of displaycycles per tick will be determined by gameroundtickspeed[v_round].
   // the number of rounds is determined by the length of gameroundtickspeed[].
   // we determine the ticks per round based on the difficulty :)
   int gameroundtickspeed[]= {0,0,0,0,0,0,0,0,0};
   switch (difficulty){
      case 1: // easy
         { int gameroundtickspeedX[]= {28,22,23,20,21,17,17,17,17};  for (int z = 0; z < 9; z++) { gameroundtickspeed[z]=gameroundtickspeedX[z];} break; } 
      case 2: // medium
         { int gameroundtickspeedX[]= {20,20,20,19,18,14,14,14,11}; for (int z = 0; z < 9; z++) { gameroundtickspeed[z]=gameroundtickspeedX[z];} break; } 
      case 3: // hard
         { int gameroundtickspeedX[]= {7,7,7,7,7,7,7,7,7}; for (int z = 0; z < 9; z++) { gameroundtickspeed[z]=gameroundtickspeedX[z];} break; } 
   }
   
   int rounds = sizeof(gameroundtickspeed)/sizeof(int);
   int shipLocation=1; int notdead=1; int idealFlikerRate=24;
   int buttonStateToggle = 1; int baddieDetails[] = {0,0,0,0,0};
    
   // play successive rounds, each terminating when either all baddies or the player dies
   for (int round = 1; round <= rounds; round++){   
      // output the round to the serial console for debug
      //Serial.print(" round = "); Serial.println(round);
      
      // baddies move closer to the player each round.  
      // and they will do that faster depending on the round number.
      // if they hit the player, then the player dies, game over etc.
      
      // we define baddie starting location based on the round.
      // note that locations < 1 is "no location" and wont be displayed at all.  
      
      // i was unable to have the switch populate baddieDetails[] directly, so that's why
      // we have this odd baddieDetails[z]=locations[z] arrangement.
      switch (round) {
         case 1: { 
            int locations[]= {15, -1, -1, -1, -1}; for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;
         case 2: { 
            int locations[]= {5, 25, -1, -1, -1}; for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;       
         case 3: { 
            int locations[]= {5, 14, 25, -1, -1}; for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;                       
         case 4: { 
            int locations[]= {4, 15, 24, -1, -1}; for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;         
         case 5: { 
            int locations[]= {4, 10, 15, 20, 24};  for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;         
         case 6: { 
            int locations[]= {3, 9, 15, 19, 23};  for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;         
         case 7: { 
            int locations[]= {4, 8, 15, 18, 24};  for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;         
         case 8: { 
            int locations[]= {3, 8, 15, 18, 23};  for (int z = 0; z <= 5; z++) { baddieDetails[z]=locations[z];} } break;         
            
      } int baddieDetailsarraylen = sizeof(baddieDetails)/sizeof(int); 
              
      while(notdead) {
         // grunt noise
         sound(1);  
         
         // update the display X times to do the flicker thing
         for (int displaycycle = 1; displaycycle <= gameroundtickspeed[round-1]; displaycycle++) {
            // we plot LEDs once per displaycycle.  we do that repeatedly to give us a picture.
            // (number of displaycycles per tick will be determined by gameroundtickspeed[v_round]
            // each plotXX() function will illuminate and also extinguish it's own LEDs
            // we want this to minimise the number of simultaneously lit leds; ie flicker.)
            
            // first plot the baddies based on baddieDetails[] and check to see if they're all dead
            notdead = PlotBaddiesAndDetectAllDead(baddieDetails, baddieDetailsarraylen, idealFlikerRate); 
            if (notdead == 0) { break; } //terminate processing the while loop
            
            // check to see if any of the baddies have hit the ground, if so terminate the game.
            int Zinstruction[] = {1, 6, 11, 16, 21};  
            for(int z = 0; z < baddieDetailsarraylen; z++) { 
               for(int q = 0; q < 5; q++){
                  if (baddieDetails[z] == Zinstruction[q]){
                     // then a baddie has hit the ground, play wah wah wahhh and end
                     sound(5); delay (500); software_Reset(); 
            }  }  }
            
            // check to see if the player is shooting and if so, plot the laser and return the path it took.
            // the toggle stuff is to make sure they're not just holding the fire button
            int buttonState = digitalRead(C_ButtonPin); 
            if (buttonState == HIGH) { 
               if (buttonStateToggle) {
                  PlotShootAndDetectHit(baddieDetails, baddieDetailsarraylen, shipLocation);
                  buttonStateToggle = 0;
               }
            } else { buttonStateToggle = 1; }
            
            // move ship and return our current location
            shipLocation=PlotAndControlShipV2(idealFlikerRate); //ie; PlotAndControlShip(delay)
                     
            // output debug stuff
            //Serial.print(" dc = "); Serial.print(displaycycle); 
            //Serial.print(" rounds = "); Serial.print(rounds); 
            //Serial.print(" round = "); Serial.println(round);
            //Serial.print(" shipLocation = "); Serial.println(shipLocation);
         } // end of displaycycle
         
         // make the baddies move closer to the player
         for(int z = 0; z < baddieDetailsarraylen; z++) { 
         baddieDetails[z]--; 
         Serial.print(" baddieDetails["); Serial.print(z); Serial.print("] "); Serial.println( baddieDetails[z]);
         }         
         //}
       } // end of roundlengthcounter, ie end of round
       
       delay(500); sound(4); delay(1000); //delay between rounds
       notdead = 1;
   } // end of round, ie end of the game.
   
   sound(6); // play endgame music   
}

void software_Reset() { 
   // Restarts program from beginning but does not reset the peripherals and registers
   // taken from http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1246541553
   asm volatile ("  jmp 0");  
}

void MakeASmiley(){
   int instructions[]= {9, 19, 2, 6, 11, 16, 22}; 
   //int instructions[]= {10, 20, 14, 22, 21, 16, 11, 6, 1, 2}; //froggy
   int instructionsArraylen = sizeof(instructions)/sizeof(int); 
   
   for (int i = 0; i < instructionsArraylen; i++){
      lightled(instructions[i],350);      
   }
   delay(200);   
   for (int flickercounter = 0; flickercounter < 1008 ; flickercounter ++) {
      // iterate through the array, starting at element 0
      for (int i = 0; i < instructionsArraylen; i++){
         lightled(instructions[i],1);
         //delay(100);
      }
   }
   delay(1000);
}

int PlotBaddiesAndDetectAllDead(int baddieDetails[], int baddieDetailsarraylen, int mydelay){
   int deadBaddieCounter=0;
   // note the last element in the array is arraylen -1.
   int lastarrayelement = baddieDetailsarraylen - 1;
                
   // iterate through the array, starting at element 1, not 0.  and while we're at it
   // we'll calculate the score too
   int score = 0;
   for (int i = 0; i < baddieDetailsarraylen; i++){
      // blink the leds according to the baddieDetails      
      if (baddieDetails[i] < 0){ // this is a dead baddie, so don't plot it.  instead count it.
         deadBaddieCounter++;
         if (deadBaddieCounter == baddieDetailsarraylen) { // all baddies are dead
            score = score + baddieDetails[i];  score = score * -1; score = 5 - score;
            Serial.print(" score = "); Serial.println( score);
            return 0;
         }
      } else {
         lightled(baddieDetails[i],mydelay);
      }
   }     
}

void PlotShootAndDetectHit(int baddieDetails[], int baddieDetailsarraylen, int shipLocation){
   //note this assumes that the ship won't leave the planet surface and that the 
   //planet surface is defined explicityly as 1, 6, 11, 16, 21.  if that changes 
   //then this will look odd.
   int shootdelay = 12;
  
   // to start with, simply play the shoot noise
   sound(2);  //shoot
   
   // then take the ship location, and for each one, calculate the laser trajectory
   // each led upon that trajectory is called laserLocation.  when we have that laserLocation
   // also iterate through the baddieDetails array to see if that laserLocation coincides with 
   // where a baddie is, if so, play the explode noise.
   switch(shipLocation){ // this will send the laser towards the baddies :)
      case 1:
         for(int laserLocation=2; laserLocation <=5; laserLocation++){ // plot the laser trajectory
            lightled(laserLocation,shootdelay); // plot the led(note singular) corresponding to the laserLocation
            for (int blc = 0; blc < baddieDetailsarraylen; blc ++){ 
               if (laserLocation == baddieDetails[blc]) { // if the laser is where a baddie is
                  sound(3);  // they hit the target!! so play the explode noise
                  baddieDetails[blc]=-1; // set that baddie to dead; ie don't display it                 
         }  }  }   
         break;
      case 6:
        for(int laserLocation=7; laserLocation <=10; laserLocation++){
            lightled(laserLocation,shootdelay); // plot the led(note singular) corresponding to the laserLocation
            for (int blc = 0; blc < baddieDetailsarraylen; blc ++){ 
               if (laserLocation == baddieDetails[blc]) { // if the laser is where a baddie is
                  sound(3);  // they hit the target!! so play the explode noise
                  baddieDetails[blc]=-1; // set that baddie to dead; ie don't display it
         }  }  }   
         break;
      case 11:
          for(int laserLocation=12; laserLocation <=15; laserLocation++){
            lightled(laserLocation,shootdelay); // plot the led(note singular) corresponding to the laserLocation
            for (int blc = 0; blc < baddieDetailsarraylen; blc ++){ 
               if (laserLocation == baddieDetails[blc]) { // if the laser is where a baddie is
                  sound(3);  // they hit the target!! so play the explode noise
                  baddieDetails[blc]=-1; // set that baddie to dead; ie don't display it
         }  }  }   
         break;
      case 16:
          for(int laserLocation=17; laserLocation <=20; laserLocation++){
            lightled(laserLocation,shootdelay); // plot the led(note singular) corresponding to the laserLocation
            for (int blc = 0; blc < baddieDetailsarraylen; blc ++){ 
               if (laserLocation == baddieDetails[blc]) { // if the laser is where a baddie is
                  sound(3);  // they hit the target!! so play the explode noise
                  baddieDetails[blc]=-1; // set that baddie to dead; ie don't display it
         }  }  }   
         break;      
      case 21:
          for(int laserLocation=22; laserLocation <=25; laserLocation++){
            lightled(laserLocation,shootdelay); // plot the led(note singular) corresponding to the laserLocation
            for (int blc = 0; blc < baddieDetailsarraylen; blc ++){ 
               if (laserLocation == baddieDetails[blc]) { // if the laser is where a baddie is
                  sound(3);  // they hit the target!! so play the explode noise
                  baddieDetails[blc]=-1; // set that baddie to dead; ie don't display it
         }  }  }   
         break;
    }
} 

int PlotAndControlShipV2(int mydelay){
  // planet surface 
  int instruction[] = {1, 6, 11, 16, 21};  
  // mycursor is a globally declared variable which stores where in the *instruction array* the ship
  // is currently residing.  it should not be confused with shipLocation which stores the LEDid of the 
  // ship location.  ie shipLocation = instruction[mycursor]
  
   int rawLocation = analogRead(InputPot); int location = map(rawLocation, 0, 1025, 0, 5);
  
   //Serial.print("rawLocation = "); Serial.print(rawLocation);  Serial.print(" location = "); Serial.println(location);

   lightled(instruction[location],mydelay);  
  
   return instruction[location];
}

void sound(int soundeffect) {
   switch( soundeffect ) {
      case 1: {//grunt
         tone(C_SpeakerPin, 17, 250);
      } break;
      case 2: {//shoot
         for (int z = 0; z < 1; z++){
            tone(C_SpeakerPin, 600, 80);      delay(40);
            tone(C_SpeakerPin, 100, 40);       delay(10);
         } 
      } break;
      case 3: {//explode
         //basically random-ish noise descending in frequency
         int freqbase=480; // where to start 
         for(int i=0; i < 15; i++){ // how many individual noises in the effect
            freqbase = freqbase - 20; // how much lower should each noise go?
            int frequency = random(freqbase-30,freqbase); //what exact freqency to plot
            int mydelay = random(30,150); // how long should each noise go for
            tone(C_SpeakerPin, frequency, mydelay); 
            Serial.print("frequency = "); Serial.print(frequency);  Serial.print(" mydelay = "); Serial.println(mydelay);
         } 
      } break;
      case 4: {// end round
         tone(C_SpeakerPin, 300, 300);delay(150);
         tone(C_SpeakerPin, 600, 100);delay(300);
         tone(C_SpeakerPin, 300, 300);delay(150);
         tone(C_SpeakerPin, 600, 800);delay(300);
      } break;
      case 5: {//you loose
         for (int z = 0; z < 1; z++){
            tone(C_SpeakerPin, 300, 80);      delay(800);
            tone(C_SpeakerPin, 275, 80);      delay(800);
            tone(C_SpeakerPin, 260, 80);      delay(800);
            for (int i = 0; i < 30; i++) {
               tone(C_SpeakerPin, 245, 15);     delay(15);  
               tone(C_SpeakerPin, 255, 15);     delay(15);                  
            }
            tone(C_SpeakerPin, 100, 40);       delay(10);
         } 
      } break;
      case 6: {//you win!
         // the song to play at the truimphant conclusion :)
         char *song = "20thCenFoxShort:d=16,o=5,b=140:b,8p,b,b,2b,p,c6,32p,b,32p,c6,32p,b,32p,c6,32p,b,8p,b,b,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,g#,32p,a,32p,b,8p,b,b,2b,4p";
         play_rtttl(song);   
      } break;
      case 7: {// game start
         //basically random-ish noise ascending in frequency
         int freqbase=180; // where to start 
         for(int i=0; i < 30; i++){ // how many individual noises in the effect
            freqbase = freqbase + 15; // how much lower should each noise go?
            int frequency = random(freqbase-30,freqbase); //what exact freqency to plot
            int mydelay = random(30,150); // how long should each noise go for
            tone(C_SpeakerPin, frequency, mydelay); 
            Serial.print("frequency = "); Serial.print(frequency);  Serial.print(" mydelay = "); Serial.println(mydelay);  
         }
         tone(C_SpeakerPin, 100, 15);     delay(250);                  
         for (int i = 0; i < 2; i++) {
            for (int i = 0; i < 4; i++) {
               tone(C_SpeakerPin, 690, 100);     delay(20);  
               tone(C_SpeakerPin, 640, 100);     delay(20);  
            }
            tone(C_SpeakerPin, 100, 15);     delay(100);                  
         }
      } break;
   }
}

void lightled(int LEDid, int ondelay) {
  //Serial.print(" led = "); Serial.print(LEDid); Serial.print("  ondelay = "); Serial.println(ondelay);
  switch (LEDid) {
    case 0:
      //all off
      digitalWrite(cathodes0, LOW); digitalWrite(annodes0, LOW); digitalWrite(cathodes1, LOW); digitalWrite(annodes1, LOW);
      digitalWrite(cathodes2, LOW); digitalWrite(annodes2, LOW); digitalWrite(cathodes3, LOW); digitalWrite(annodes3, LOW);
      digitalWrite(cathodes4, LOW); digitalWrite(annodes4, LOW); break;
    case 1: //c0a0
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes0, HIGH); break;
    case 2: //c0a1
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes1, HIGH); break;
    case 3: //c0a2
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes2, HIGH); break;
    case 4: //c0a3
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes3, HIGH); break;      
    case 5: //c0a4
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes4, HIGH); break;
    case 6: //c1a0
      digitalWrite(cathodes1, HIGH); digitalWrite(annodes0, HIGH); break;
    case 7: //c1a1
      digitalWrite(cathodes1, HIGH); digitalWrite(annodes1, HIGH); break;
    case 8: //c1a2
      digitalWrite(cathodes1, HIGH); digitalWrite(annodes2, HIGH); break;
    case 9: //c1a3
      digitalWrite(cathodes1, HIGH); digitalWrite(annodes3, HIGH); break;
    case 10: //c1a4
      digitalWrite(cathodes1, HIGH); digitalWrite(annodes4, HIGH); break;
    case 11: //c2a0
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes0, HIGH); break;
    case 12: //c2a1
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes1, HIGH); break;
    case 13: //c2a2
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes2, HIGH); break;
    case 14: //c2a3
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes3, HIGH); break;
    case 15: //c2a4
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes4, HIGH); break;
    case 16: //c3a0
      digitalWrite(cathodes3, HIGH); digitalWrite(annodes0, HIGH); break;
    case 17: //c3a1
      digitalWrite(cathodes3, HIGH); digitalWrite(annodes1, HIGH); break;
    case 18: //c3a2
      digitalWrite(cathodes3, HIGH); digitalWrite(annodes2, HIGH); break;
    case 19: //c3a3
      digitalWrite(cathodes3, HIGH); digitalWrite(annodes3, HIGH); break;
    case 20: //c3a4
      digitalWrite(cathodes3, HIGH); digitalWrite(annodes4, HIGH); break;
    case 21: //c4a0
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes0, HIGH); break;
    case 22: //c4a1
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes1, HIGH); break;
    case 23: //c4a2
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes2, HIGH); break;
    case 24: //c4a3
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes3, HIGH); break;
    case 25: //c4a3
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes4, HIGH); break;
    case 88: //do nothing.
      break;  
    case 100:
      //all on
      digitalWrite(cathodes0, HIGH); digitalWrite(annodes0, HIGH); digitalWrite(cathodes1, HIGH); digitalWrite(annodes1, HIGH);
      digitalWrite(cathodes2, HIGH); digitalWrite(annodes2, HIGH); digitalWrite(cathodes3, HIGH); digitalWrite(annodes3, HIGH);
      digitalWrite(cathodes4, HIGH); digitalWrite(annodes4, HIGH);    
      break;      
    default: // if nothing else matches, do the default
      //Serial.print("ERROR LEDid = "); Serial.println(LEDid); alloff(); allon(); delay(1500);
      break;
  }
  //delay a sec before resuming
  delay(ondelay);  alloff(); 
}

void alloff(){
  // turn everything off for contrast
  digitalWrite(cathodes0, LOW);  digitalWrite(cathodes1, LOW);  digitalWrite(cathodes2, LOW);  digitalWrite(cathodes3, LOW);  digitalWrite(cathodes4, LOW);
  digitalWrite(annodes0, LOW);  digitalWrite(annodes1, LOW);  digitalWrite(annodes2, LOW);  digitalWrite(annodes3, LOW);  digitalWrite(annodes4, LOW); 
  digitalWrite(IndicatorLED, LOW);
}

void allon(){
  // turn everything off for contrast
  digitalWrite(cathodes0, HIGH);  digitalWrite(cathodes1, HIGH);  digitalWrite(cathodes2, HIGH);  digitalWrite(cathodes3, HIGH);  digitalWrite(cathodes4, HIGH);
  digitalWrite(annodes0, HIGH);  digitalWrite(annodes1, HIGH);  digitalWrite(annodes2, HIGH);  digitalWrite(annodes3, HIGH);  digitalWrite(annodes4, HIGH); 
  digitalWrite(IndicatorLED, HIGH);
}

void SoundEffectGenViaPot(){
 //generate sounds using input pot
  int buttonState = digitalRead(C_ButtonPin);
  int frequencyX = analogRead(InputPot); int frequency = map(frequencyX, 0, 1023, 1, 6000);
   if (buttonState == HIGH) { 
      //tone(C_SpeakerPin, frequency, 200);
      tone(C_SpeakerPin, frequency, 40);      //delay(10);
      tone(C_SpeakerPin, frequency-200, 40);      //delay(10);
      tone(C_SpeakerPin, frequency, 40);      //delay(10);
      //tone(C_SpeakerPin, 100, 40);       delay(5);
  }
  Serial.print("frequency = "); Serial.println(frequency);
}

void  basicMultiplexing(){
  // for our matrix 24 is a good delay, but more leds means more flicker.  simple.
  int ondelayX = analogRead(InputPot); int ondelay = map(ondelayX, 0, 1023, 0, 100);

  for (int i = 1; i <= 25; i++){lightled(i,ondelay);}
  Serial.print("ondelay = "); Serial.println(ondelay);
}

int PlotAndControlShipVelocity(int mydelay){
  // planet surface 
  int instruction[] = {1, 6, 11, 16, 21};  
  // mycursor is a globally declared variable which stores where in the *instruction array* the ship
  // is currently residing.  it should not be confused with shipLocation which stores the LEDid of the 
  // ship location.  ie shipLocation = instruction[mycursor]
  
  // note the last element in the array is arraylen -1 !!!!
  int arraylen = sizeof(instruction)/sizeof(int); int lastarrayelement = arraylen - 1;
  // determine the position of the pot and assign a velocity to it
  int velocityX= analogRead(InputPot); int velocity = map(velocityX, 0, 1023, -150, 150);
  // set the cursor to increment or decrement depending on the velocity, note the deadzone
  if (velocity > 15) { mycursor++;} 
  if (velocity < -15) {mycursor--;}
  
  // what do we do when they reach the edge of the play area?  to wrap or not to wrap?
  // wrap:
  // if (mycursor < 0) { mycursor = lastarrayelement; } if (mycursor > lastarrayelement) { mycursor = 0; }
  // not wrap:
  if (mycursor < 0) { mycursor = 0; } if (mycursor > lastarrayelement) { mycursor = lastarrayelement; }
  
  //Serial.print("mycursor = "); Serial.print(mycursor);  Serial.print(" mydelay = "); Serial.print(mydelay);  Serial.print(" velocity = "); Serial.print(velocity); 
       
  // blink the leds according to the instructions
  lightled(instruction[mycursor],mydelay);
  
  //ie shipLocation = instruction[mycursor]  
  return instruction[mycursor];
}

int SimpleVelocityLoop(){
   // define the instructions for the array and the delay for led illumination
   //int instruction[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,0,100,0,100}; //test
   //int instruction[] = {1, 6, 11, 16, 21, 22, 23, 24, 25, 20, 15, 10, 5, 4, 3, 2, 7, 12, 17, 18, 19, 14, 9, 8, 13, 7}; // spiral and exit
   int instruction[] = {1, 6, 11, 16, 21, 22, 23, 24, 25, 20, 15, 10, 5, 4, 3, 2}; // outside loop
   //int instruction[] = {1, 6, 7, 11, 16, 12, 21, 22, 17, 23, 24, 18, 25, 20, 19, 15, 10, 14, 5, 4, 9, 3, 2, 8}; // 2 square loop

   // and then simply iterate through the instruction array
   int i; int arraylen = sizeof(instruction)/sizeof(int);
   
   int mydelayX = 0;
   int buttonState = 0;
   
   while(buttonState != HIGH){ 
      for (i = 0; i < arraylen; i = i + 1) {
         buttonState = digitalRead(C_ButtonPin); if (buttonState) { break; }
         // delay is used for setting both the ON and OFF durations
         mydelayX= analogRead(InputPot); int mydelay = map(mydelayX, 0, 1023, 4, 200);  
         // blink the leds according to the instructions
         // sound(1);
         lightled(instruction[i],mydelay);
         // turn everything off and wait a sec (before re-reading the instructinos and starting again etc)
         alloff(); delay(mydelay/2);
         //Serial.print("mydelayX = "); Serial.print(mydelayX);
      }   
   }
   return mydelayX;
}

void play_rtttl(char *p) {
  // this function based on the LCA 2012 leo stick tunes collaborative effort at https://gist.github.com/1800871
    
  //char *song = "Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c";
  //char *song = "Looney:d=4,o=5,b=140:32p,c6,8f6,8e6,8d6,8c6,a.,8c6,8f6,8e6,8d6,8d#6,e.6,8e6,8e6,8c6,8d6,8c6,8e6,8c6,8d6,8a,8c6,8g,8a#,8a,8f";
  //char *song = "20thCenFox:d=16,o=5,b=140:b,8p,b,b,2b,p,c6,32p,b,32p,c6,32p,b,32p,c6,32p,b,8p,b,b,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,b,32p,g#,32p,a,32p,b,8p,b,b,2b,4p,8e,8g#,8b,1c#6,8f#,8a,8c#6,1e6,8a,8c#6,8e6,1e6,8b,8g#,8a,2b";
  //char *song = "Canon:d=4,o=5,b=80:8d,8f#,8a,8d6,8c#,8e,8a,8c#6,8d,8f#,8b,8d6,8a,8c#,8f#,8a,8b,8d,8g,8b,8a,8d,8f#,8a,8b,8f#,8g,8b,8c#,8e,8a,8c#6,f#6,8f#,8a,e6,8e,8a,d6,8f#,8a,c#6,8c#,8e,b,8d,8g,a,8f#,8d,b,8d,8g,c#.6";
  //char *song = "StarWars:d=4,o=5,b=45:32p,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#.6,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#6";
  //char *song = "Gadget:d=16,o=5,b=50:32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,32d#,32f,32f#,32g#,a#,d#6,4d6,32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,8d#";
  //char *song = "Smurfs:d=32,o=5,b=200:4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8f#,p,8a#,p,4g#,4p,g#,p,a#,p,b,p,c6,p,4c#6,16p,4f#6,p,16c#6,p,8d#6,p,8b,p,4g#,16p,4c#6,p,16a#,p,8b,p,8f,p,4f#";
  //char *song = "LeisureSuit:d=16,o=6,b=56:f.5,f#.5,g.5,g#5,32a#5,f5,g#.5,a#.5,32f5,g#5,32a#5,g#5,8c#.,a#5,32c#,a5,a#.5,c#.,32a5,a#5,32c#,d#,8e,c#.,f.,f.,f.,f.,f,32e,d#,8d,a#.5,e,32f,e,32f,c#,d#.,c#";
    
  #define isdigit(n) (n >= '0' && n <= '9')
   
  #define NOTE_B0 31
  #define NOTE_C1 33
  #define NOTE_CS1 35
  #define NOTE_D1 37
  #define NOTE_DS1 39
  #define NOTE_E1 41
  #define NOTE_F1 44
  #define NOTE_FS1 46
  #define NOTE_G1 49
  #define NOTE_GS1 52
  #define NOTE_A1 55
  #define NOTE_AS1 58
  #define NOTE_B1 62
  #define NOTE_C2 65
  #define NOTE_CS2 69
  #define NOTE_D2 73
  #define NOTE_DS2 78
  #define NOTE_E2 82
  #define NOTE_F2 87
  #define NOTE_FS2 93
  #define NOTE_G2 98
  #define NOTE_GS2 104
  #define NOTE_A2 110
  #define NOTE_AS2 117
  #define NOTE_B2 123
  #define NOTE_C3 131
  #define NOTE_CS3 139
  #define NOTE_D3 147
  #define NOTE_DS3 156
  #define NOTE_E3 165
  #define NOTE_F3 175
  #define NOTE_FS3 185
  #define NOTE_G3 196
  #define NOTE_GS3 208
  #define NOTE_A3 220
  #define NOTE_AS3 233
  #define NOTE_B3 247
  #define NOTE_C4 262
  #define NOTE_CS4 277
  #define NOTE_D4 294
  #define NOTE_DS4 311
  #define NOTE_E4 330
  #define NOTE_F4 349
  #define NOTE_FS4 370
  #define NOTE_G4 392
  #define NOTE_GS4 415
  #define NOTE_A4 440
  #define NOTE_AS4 466
  #define NOTE_B4 494
  #define NOTE_C5 523
  #define NOTE_CS5 554
  #define NOTE_D5 587
  #define NOTE_DS5 622
  #define NOTE_E5 659
  #define NOTE_F5 698
  #define NOTE_FS5 740
  #define NOTE_G5 784
  #define NOTE_GS5 831
  #define NOTE_A5 880
  #define NOTE_AS5 932
  #define NOTE_B5 988
  #define NOTE_C6 1047
  #define NOTE_CS6 1109
  #define NOTE_D6 1175
  #define NOTE_DS6 1245
  #define NOTE_E6 1319
  #define NOTE_F6 1397
  #define NOTE_FS6 1480
  #define NOTE_G6 1568
  #define NOTE_GS6 1661
  #define NOTE_A6 1760
  #define NOTE_AS6 1865
  #define NOTE_B6 1976
  #define NOTE_C7 2093
  #define NOTE_CS7 2217
  #define NOTE_D7 2349
  #define NOTE_DS7 2489
  #define NOTE_E7 2637
  #define NOTE_F7 2794
  #define NOTE_FS7 2960
  #define NOTE_G7 3136
  #define NOTE_GS7 3322
  #define NOTE_A7 3520
  #define NOTE_AS7 3729
  #define NOTE_B7 3951
  #define NOTE_C8 4186
  #define NOTE_CS8 4435
  #define NOTE_D8 4699
  #define NOTE_DS8 4978  
  #define OCTAVE_OFFSET 0
  
  int notes[] = { 0,
  NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4,
  NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5,
  NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6,
  NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7
  };
    // Absolutely no error checking in here
  
    byte default_dur = 4;
    byte default_oct = 6;
    int bpm = 63;
    int num;
    long wholenote;
    long duration;
    byte note;
    byte scale;
  
    // format: d=N,o=N,b=NNN:
    // find the start (skip name, etc)
  
    while(*p != ':') p++; // ignore name
    p++; // skip ':'
  
    // get default duration
    if(*p == 'd'){
      p++; p++; // skip "d="
      num = 0;
      while(isdigit(*p)){
        num = (num * 10) + (*p++ - '0');
      }
      if(num > 0) default_dur = num;
      p++; // skip comma
    }
  
  
    // get default octave
    if(*p == 'o'){
      p++; p++; // skip "o="
      num = *p++ - '0';
      if(num >= 3 && num <=7) default_oct = num;
      p++; // skip comma
    }
  
    // get BPM
    if(*p == 'b'){
      p++; p++; // skip "b="
      num = 0;
      while(isdigit(*p)){
        num = (num * 10) + (*p++ - '0');
      }
      bpm = num;
      p++; // skip colon
    }
    
    // BPM usually expresses the number of quarter notes per minute
    wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole note (in milliseconds)
    
    // now begin note loop
    while(*p){
      // first, get note duration, if available
      num = 0;
      while(isdigit(*p)){
        num = (num * 10) + (*p++ - '0');
      }
      
      if(num) duration = wholenote / num;
      else duration = wholenote / default_dur; // we will need to check if we are a dotted note after
  
      // now get the note
      note = 0;
      
      switch(*p) {
        case 'c':
          note = 1;
          break;
        case 'd':
          note = 3;
          break;
        case 'e':
          note = 5;
          break;
        case 'f':
          note = 6;
          break;
        case 'g':
          note = 8;
          break;
        case 'a':
          note = 10;
          break;
        case 'b':
          note = 12;
          break;
        case 'p':
        default:
          note = 0;
      }
      p++;
  
      // now, get optional '#' sharp
      if(*p == '#'){
        note++;
        p++;
      }
  
      // now, get optional '.' dotted note
      if(*p == '.'){
        duration += duration/2;
        p++;
      }
    
      // now, get scale
      if(isdigit(*p)){
        scale = *p - '0';
        p++;
      }
      else{
        scale = default_oct;
      }
  
      scale += OCTAVE_OFFSET;
  
      if(*p == ',')
        p++; // skip comma for next note (or we may be at the end)
  
      // now play the note
  
      if(note){
        digitalWrite(C_SpeakerPin, HIGH);
        int danFreq;
        float danDur;
        danFreq = notes[(scale - 4) * 12 + note];
        danDur = 1000000 / danFreq;
        unsigned long start = millis();
        while (millis() - start <= duration) {
            digitalWrite(C_SpeakerPin, HIGH);
            delayMicroseconds(danDur);
            digitalWrite(C_SpeakerPin, LOW);
            delayMicroseconds(danDur);
        }
        digitalWrite(C_SpeakerPin, LOW);
      }
      else{
        delay(duration);
      }
    }
}