Introduction: Automated ATtiny Marble Labyrinth

After purchasing a marble labyrinth from Amazon, I had seen others use Arduino microcontrollers and servos to automate a maze. Seeing that Arduino and other large microcontroller boards were too big to fit in the maze I found that an ATtiny84 and 85 could stably move servos and would be small enough to fit inside the maze.

It took some finessing but I also got a few rtttl music files to fit in the ATtiny85 so that music would play while using the maze. Playing the music gives the maze more of an arcade feel. I used toons like "Mission Impossible" and "Indiana Jones" to add an exciting twist while playing, but just about any song can be converted or found in rtttl form. The last thing I added is LED's to light up the underbelly. Using red and blue LEDs gives the feel of progressing through levels as the user gets closer to the end.

Supplies


  • ATTINY85
  • ATTINY84
  • Arduino Uno
  • RTTTL BUZZER
  • Adafruit analog Joystick
  • 10k Ohm Trim pot
  • Power USB cord
  • Hot Glue

Step 1: Learn to Program ATtiny84 & 85

To program the ATtiny84 and 85 using an Arduino I used this video by Matt Inglot that points to all of the places to get the libraries and schematics needed to hook up the ATtiny's. It's a journey even with this video to get the ATtiny to program but refer back to the video as much as you can before giving up. You can also try verifying it programmed with simpler code as the code I have uploaded needs a few libraries downloaded to work.

Warning

I set the clock to 8MHz internal, do not set the clock to something else if you don't know what it does. I set one of my ATtiny84's to something else and couldn't get it to read or program anymore.

Step 2: Programming Cable

To keep the programming easy, I taped the pins in the right order after getting a successful program.

Step 3: Wiring

Having a schematic helped greatly to solder all of the connections correctly the first time. I used Fritzing to make mine. You can pay for the updated version or download an older one for free.

Step 4: Assembling

Using mainly hot glue, I adhered wires and components on the side walls low enough to be out of the way of the playing field. The servos were screwed on to allow for a better hold. To control the maze with the servo, I placed and glued a servo arm to the bar using JB weld, which worked great.

In the photo with the servo you can see I glued a servo arm to the control bar, try widening the hole on the arm to be as tight a fit as possible, this will help the glue joint not have to be stressed as much. I then took 2 four inch long copper wires and took off all of the insulation. After, I threaded them through both end holes of the servo arm on the bar and the servo arm on the servo then looped them through the arm attached to the bar. Tighten both ends of the arms with the wire so the two arms are parallel to each other and solder the ends together. I used the wire so it would have enough rigidity to push as well as pull but string could work as well.

Step 5: Music Code for ATtiny85

For ATtiny84 to play the music


//#include <Tone.h> // the core tone()/noTone() are used.
#include <rtttl.h>
#define OCTAVE_OFFSET 0
int led = 0;     // the PWM pin the LED is attached to
int brightness = 0; // how bright the LED is
int fadeAmount = 3;
const int pinSpeaker = 0;
const int octave = random(0, 1);
//const int duo = random(0, 1);

// this solution is recommended:
// the song is stored in program memory only
const char song[] PROGMEM = "Indiana:d=4,o=5,b=250:e,8p,8f,8g,8p,1c6,8p.,d,8p,8e,1f,p.,g,8p,8a,8b,8p,1f6,p,a,8p,8b,2c6,2d6,2e6,e,8p,8f,8g,8p,1c6,p,d6,8p,8e6,1f.6,g,8p,8g,e.6,8p,d6,8p,8g,e.6,8p,d6,8p,8g,f.6,8p,e6,8p,8d6,2c6";
const char song_1[] PROGMEM = "smbdeath:d=4,o=5,b=90:8p,16b,16f6,16p,16f6,16f.6,16e.6,16d6,16c6,16p,16e,16p,16c";
const char song_2[] PROGMEM = "SMBunderground:d=16,o=6,b=100:c5,a5,a,a#5,a#,2p,8p,c,c5,a5,a,a#5,a#,2p,8p,f5,f,d5,d,d#5,d#,2p,32d#,d,32c#,c,p,d#,p,d,p,g#5,p,g5,p,c#,p,32c,f#,32f,32e,a#,32a,g#,32p,d#,b5,32p,a#5,32p,a5,g#5";
const char song_3[] = "SongOfTh:d=4,o=6,b=140:8f5,8a5,b5,8f5,8a5,b5,8f5,8a5,8b5,8e,d,8b5,8c,8b5,8g5,2e5,8p,8d5,8e5,8g5,2e5,8p,8d5,8e5,f5,8g5,8a5,b5,8c,8b5,e5,8p,8d5,8c5,8f5,8e5,8g5,8f5,8a5,8g5,16a5,16a5,16a5,16g5,2a5";
const char song_4[] PROGMEM = "smb_under:d=4,o=6,b=100:32c,32p,32c7,32p,32a5,32p,32a,32p,32a#5,32p,32a#,2p,32c,32p,32c7,32p,32a5,32p,32a,32p,32a#5,32p,32a#,2p,32f5,32p,32f,32p,32d5,32p,32d,32p,32d#5,32p,32d#,2p,32f5,32p,32f,32p,32d5,32p,32d,32p,32d#5,32p,32d#";
const char song_6[] PROGMEM = "StarWars:d=4,o=5,b=80:8d.,8d.,8d.,8a#4,16f,8d.,8a#4,16f,d.,32p,8a.,8a.,8a.,8a#,16f,8c#.,8a#4,16f,d.,32p,8d.6,8d,16d,8d6,32p,8c#6,16c6,16b,16a#,8b,32p,16d#,8g#,32p,8g,16f#,16f,16e,8f,32p,16a#4,8c#,32p,8a#4,16c#,8f.,8d,16f,a.,32p,8d.6,8d,16d,8d6,32p,8c#6,16c6,16b,16a#,8b,32p,16d#,8g#,32p,8g,16f#,16f,16e,8f,32p,16a#4,8c#,32p,8a#4,16f,8d.,8a#4,16f,d.";
const char song_7[] = "PiriteTheme:d=4,o=6,b=200:8d,8e,2f,8g,8a,g,f,e,f,g,a,g,p,8f,8g,a,p,8g,8f,e,f,e,d,p,8e,8c,d,8p,p,8d,8e,f,p,8e,8f,g,f,g,a,g,f,d";
const char song_11[] PROGMEM = "StarWars:d=4,o=5,b=250:8a,8p,8d6,8p,8a,8p,8d6,8p,8a,8d6,8p,8a,8p,8g#,a,8a,8g#,8a,g,8f#,8g,8f#,f.,8d.,16p,p.,8a,8p,8d6,8p,8a,8p,8d6,8p,8a,8d6,8p,8a,8p,8g#,8a,8p,8g,8p,g.,8f#,8g,8p,8c6,a#,a,g";
const char song_13[] PROGMEM = "SuperMar:d=4,o=5,b=140:a,8f.,16c,16d,8f,f,16d,8c,8f,8f,8c6,8a.,g,8p,a,8f.,16c,16d,8f,f,16d,8c,8f,16a#,16a,16g,f,8p,8a.,8f.,8c,8a.,f,16g#,16f,8c,8g#.,g,8p,8a.,8f.,8c,8a.,f,16g#,16f,8c,c6";
const char song_12[] PROGMEM = "Pacman:d=32,o=5,b=112:32p,b,p,b6,p,f#6,p,d#6,p,b6,f#6,16p,16d#6,16p,c6,p,c7,p,g6,p,e6,p,c7,g6,16p,16e6,16p,b,p,b6,p,f#6,p,d#6,p,b6,f#6,16p,16d#6,16p,d#6,e6,f6,p,f6,f#6,g6,p,g6,g#6,a6,p,b.6";


//int n = 0;
//int quit = false;

//char buffer[150];
Rtttl player;


void setup(void)
{
player.begin(pinSpeaker);
 pinMode(led, OUTPUT);
 pinMode(pinSpeaker, OUTPUT);  
 player.play_P(song_12, octave);
  delay(2000);
}

void loop(void)
{
 player.play_P(song, octave);
  delay(4000);
 player.play_P(song_11, octave);
  delay(4000);
 player.play_P(song_13, octave);
  delay(4000);
 player.play_P(song_4, octave);
  delay(4000);
 player.play_P(song_2, octave);
  delay(4000);
 player.play_P(song_1, octave);
  delay(30000);
  
 // analogWrite(led, brightness);
 // brightness = brightness + fadeAmount;
 // if (brightness <= 0 || brightness >= 255) {
 //  fadeAmount = -fadeAmount;
 // }
 // delay(30);
}

Step 6: ATtiny84 Servo Code

For ATtiny84 to move servos

LINK This link is the site where I downloaded my rtttl music from.

#include <TinyServo.h>

const byte SERVOS = 2;// how many servos do you have?
const byte servoPin[SERVOS] = {7,6}; // what pins are your servos on?
int analog1 = A4;// pins for joystick
int analog2 = A5;// pins for joystick

#define SERVO1 0 // select first spot in servo pin {0,1}
#define SERVO2 1 // select second spot in servo pin

int joyValx;
int joyValy;
void setup()
{
 pinMode(7, OUTPUT); //Set pins as receive or send
 pinMode(6, OUTPUT);
 pinMode(analog1, INPUT);
 pinMode(analog1, INPUT);
 setupServos();
 // Serial.begin(9600); //Un-comment to see reading in a Serial window
}

void loop()
{
 joyValx = analogRead(analog1);
 joyValx = map (joyValx, 0, 1023, 25, 165); //Play with the second set of numbers to get the servo to only turn as far as // the gam can go.
 moveServo(SERVO1, joyValx);
 // Serial.print("X= "); //Serial read the output to help troubleshoot
 // Serial.println(joyValx);
 
 joyValy = analogRead(analog2);
 joyValy = map (joyValy, 0, 1023, 25, 165); // First set of numbers should be min and max reading of your 10k pot
 moveServo(SERVO2, joyValy);
 //Serial.print("Y= ");
 //Serial.println(joyValy);
 delay(15);
}