Automated Model Rollercoaster

About a year ago I started thinking about a way of automatising my model rollercoasters. After asking around I got to buying an Arduino.

Now 1 year later I have a result. Many setbacks and difficulties along the way though.

I’ll let this video do the job for now.

Powered by an Arduino Mega. Motors are controlled using relays and transistors. Sensors are basic micro switches with an extra long handle.

Second I would like to thank Pieter Floris (www.floris.cc). I bought the Arduino through his webshop and he has been a great help finding solutions to problems. His customer service is superb and I would recommend anyone living in Holland or Belgium to check out his website and get involved in the magical world of the Arduino!

Note, comments are in Dutch, most of them are for debugging. The project is supposed to be versatile, so I’ll leave them in so I can quickly find what everything does.
It’s not entirely optimised and a few parts are left overs from debugging. However they do not affect the program itself.

#include <Servo.h>

//Servos
Servo gatesL;
Servo gatesR;

//Pins
const int modeSwitch      = 22; // Sleutel schakelaar, kijkt in welke gebruiks modus de achtbaan moet werken.
const int gateSwitch      = 26; // een draaischakelaar die de poortjes bedient.
const int dispatchButton  = 28; // De knop die de trein laat vertrekken.
const int ledG            = 34; // Dispatch knop lampje
const int ledR            = 36; // Zolang de poortjes open zijn moet deze lamp branden
const int M12             = 42; // Motoren 1 en 2 (via relais) (lift+station)
const int M34             = 44; // Motoren 3 en 4 (via relais) (lift+station)
const int M56             = 46; // Motoren 5 en 6 (via relais) (remmen)
const int Sensor1         = 49; // Deze sensor zit bovenaan de lift
const int Sensor2         = 51; // Deze sensor zit op de remmen
const int Sensor3         = 53; // Deze sensor zit in het station



//Variables
int modeState;
int gateState;
int dispatchState;
int liftSensor;
int brakeSensor;
int stationSensor;
int ledGstate = LOW;

long previousMillis   = 0;
long interval         = 1000;



void setup() {
    Serial.begin(9600);
    //Input

    pinMode(modeSwitch, INPUT);
    pinMode(gateSwitch, INPUT);
    pinMode(dispatchButton, INPUT);
    pinMode(Sensor1, INPUT);
    pinMode(Sensor2, INPUT);
    pinMode(Sensor3, INPUT);

    //Output

    pinMode(ledG, OUTPUT);
    pinMode(ledR, OUTPUT);
    pinMode(M12, OUTPUT);
    pinMode(M34, OUTPUT);
    pinMode(M56, OUTPUT);
    gatesR.attach(2);
    gatesL.attach(3);

    //Initialize
    digitalWrite(ledG, LOW);
    digitalWrite(ledR, LOW);
}



void loop() {
  checkMode();
}


//Kijk of de schakelaar op Manual of Auto staat, ga aan de hand van de stand van deze knop door naar de betreffende modus.

void startupWait() {
  delay(100);
  checkMode();
}

void checkMode() {
  delay(1000);
  modeState = digitalRead(modeSwitch);
  if (modeState == HIGH) {
    Serial.println("MANUAL ----------------");
    manualwaitMode();
  }
  else {
    Serial.println("AUTO ++++++++++++++++++");
    autowaitMode();
  }
}




 //Manual Mode

//Kijk of de poortjes open of dicht zijn, als de poortjes open zijn gaat het rode lampje branden en blijft de groene uit. 
//Als de poortjes dicht zijn gaat hij door naar de volgende functie.

void manualwaitMode() {
  delay(100);
  manualMode();
}

void autowaitMode() {
  delay(100);
  autoMode();
}

void manualMode() {
   gateState = digitalRead(gateSwitch);

delay(100);

//Poortjes open
   if (gateState == LOW) {
       gatesR.write(1);
       gatesL.write(1);
       digitalWrite(ledR, HIGH);
       digitalWrite(ledG, LOW);
   }   
//Poortjes dicht
   else {
       gatesR.write(90);
       gatesL.write(90);
       digitalWrite(ledR, LOW);
       digitalWrite(ledG, LOW);
       dispatchMode();
   }
}




// Wanneer de knop ingedrukt is vertrekt de trein, de groene lamp gaat aan en blijft aan. Ook moet hij de sensor op de lift gaan checken. 
//Indien de knop niet ingedrukt is en de poortjes dicht zijn moet de groene lamp knipperen.

void dispatchMode() {
  dispatchState = digitalRead(dispatchButton);

//Knop ingedrukt

  if (dispatchState == LOW) {
    Serial.println("Dispatch knop is een keer ingedrukt.");
    digitalWrite(ledG, HIGH);
    digitalWrite(M12, HIGH);
    digitalWrite(M34, HIGH);
    checkLift();    
  }

//Knop niet ingedrukt

  else {
    blinkG();
  }
}




//Als de poortjes dicht zijn en de knop niet ingedrukt is blijft het lampje knipperen.

void blinkG() {

 if (millis() - previousMillis > interval) {
     // save the last time you blinked the LED
     previousMillis = millis();  

     // if the LED is off turn it on and vice-versa:
     if (ledGstate == LOW)
       ledGstate = HIGH;
     else
       ledGstate = LOW;

     // set the LED with the ledState of the variable:
     digitalWrite(ledG, ledGstate);
   } 

}




//Waneer deze functie bereikt word moet hij de sensor blijven lezen todat hij aangeraakt is door de trein, pas dan mag het programma verder naar de volgende stap.
//Zoland de sensor niet aangeraakt is blijft hij deze stap herhalen.
//Wanneer de sensor aangeraakt is blijven de motoren nog eventjes door draaien en gaat daarna ook de groene lamp uit.Hierna moet hij door naar de volgende functie.
// N.B. De volgende functies (checkSensor) worden zowel door Manual als Auto gebruikt.

void checkLift() {
  liftSensor = digitalRead(Sensor1);

//De trein is de sensor nog niet gepaseerd.

  while(liftSensor == HIGH) {
    Serial.println("Trein beklimt de lift.");
    delay(100);
    checkLift();
  }

//De trein paseert de sensor

  if(liftSensor == LOW) {
    Serial.println("Trein passeert de top van de lift.");
    delay(5000);
    digitalWrite(ledG, LOW);
    digitalWrite(M12, LOW);
    digitalWrite(M34, LOW);
    checkBrake();
  }
}




//Zodra bij deze functie komt moet hij deze blijven herhalen todat de trein de sensor is gepaseerd. Pas dan mag hij door naar de volgende stap.
//Als de sensor is aangeraakt moeten er motoren gaan draaien en blijven draaien tot hij bij de volgende functie is.


void checkBrake() {
  brakeSensor = digitalRead(Sensor2);

//Sensor nog niet aangeraakt

  while(brakeSensor == HIGH) {
    Serial.println("Trein volgt de rest van de baan.");
    delay(100);
    checkBrake();
  }

//Trein passeert de sensor

  if(brakeSensor == LOW) {
    Serial.println("Trein rijdt de remmen in.");
    digitalWrite(M56, HIGH);
    checkStation();
  }
}




//Deze functie moet zich blijven herhalen todat de trein de sensor is gepaseerd. Pas dan mag het programma opnieuw beginnen en kijken in welke gebruiks modus hij staat.
//Als de sensor is aangeraakt moeten de motoren stoppen met draaien en gaat hij terug naar de eerste functie.

void checkStation() {
  stationSensor = digitalRead(Sensor3);

//Sensor is nog niet aangeraakt

  while(stationSensor == HIGH) {
    Serial.println("Trein is nog niet in het station.");
    delay(1000);
    checkStation();
  }

//Sensor is aangeraakt

  if(stationSensor == LOW) {
    Serial.println("Trein is wel in het station.");
    digitalWrite(M56, LOW);
    delay(100);
/* restart program */
    restartP();
  }
}




 //Auto

//De volgorde waarin de achtbaan dingen moet doen staat hierin gedefinieerd. Poortjes open, poortjes dicht, vertrekken. Zodra deze functie afgelopen is moet hij kijken of de trein al bovenaan de lift is.

  void autoMode() {

    delay(1000);

    digitalWrite(ledG, HIGH);

    delay(1000);

    digitalWrite(ledG, LOW);

    delay(1000);

    digitalWrite(ledG, HIGH);

    delay(1000);

    digitalWrite(ledG, LOW);

    delay(1000);

    gatesR.write(1);

    gatesL.write(1);

    digitalWrite(ledR, HIGH);

    delay(10000);

    gatesR.write(90);

    gatesL.write(90);

    digitalWrite(ledR, LOW);

    delay(1000);

    digitalWrite(ledG, HIGH);

    delay(1000);

    digitalWrite(ledG, LOW);

    delay(1000);

    digitalWrite(ledG, HIGH);

    delay(1000);

    digitalWrite(ledG, LOW);

    delay(1000);

    digitalWrite(ledG, HIGH);

    digitalWrite(M12, HIGH);

    digitalWrite(M34, HIGH);

    checkLift();

 }
 

void restartP() // Restarts program from beginning but does not reset the peripherals and registers
{
 asm volatile ("  jmp 0");  
}

Looks awesome!

I wish I had the room to set up my old Spacewarp ball coasters; I loved those things as a kid...

:)

Hahaha, that's really cool.

Nice work!

Pretty cool. Are you going to expand it to a full room size ( if possible )?

The coaster is interchangeable of course. This year I will be displaying this coaster at a funfair exposition and so are 2 of my friends. The electronics will be featured on the largest coaster which is currently under construction. The final coaster will have a working station and a tiredrive after the brakes to get the train back to the start. However this was just for testing, and my coaster doesn't have a tiredrive system.

If you are interested I will post a video when the funfair expo is over ;) Have to wait another month though ;)

Edit: I forgot to mention.

The coaster is powered by an Arduino mega, and the lights that are on the looping of the coaster are also powered by an Arduino!

Hi Maxlaam, despite the "Many setbacks and difficulties along the way" ... you can be proud of what you have achieved.

The control box looks professional!. And using a parallel printer port + cable to connect the boxes is quite clever.

The videos are interesting too. Please upload one of the funfair 'result'.

I wish I had the room to set up my old Spacewarp ball coasters; I loved those things as a kid...

I love those ball machines that you can get. I so need to build myself one...

:)

I love those ball machines that you can get. I so need to build myself one...

I still have my original Bandai kits; they're packed away, awaiting a day I can pull them out and rebuild them.

The crazy thing is, the Knex coasters take up even more space!

There was a guy (I've long lost the link) who had a website where he detailed constructing working scale model coasters; they were completely hand-built, some were based on actual coasters, others were his own creations. Museum quality stuff, really.

Apparently, there is a real passion out there by many people to simulate and build roller coasters; a few different coaster simulators exist (all inspired originally by Disney's Coaster software for DOS; I have a copy of that somewhere, too) - and more than one person has built their own backyard coaster (including one crazy guy who made a vertical loop track).

:)

I wish I had the room to set up my old Spacewarp ball coasters

You probably do, if you pick the right layout. I have mine set up permanently on a 3-foot piece of 1x8 pine: I just screwed down the baseplates. It even survived the move from CA with almost no damage: just a few parts near one end knocked loose.

You probably do, if you pick the right layout.

That's the problem - I want the full deal; see, somehow back in the day I managed to convince my parents to get me the "big one" - I can't remember the number off hand, but it was like 4x5 baseplates with three or four tracks, double spiral loops, etc - it was the largest Spacewarp kit that Bandai sold back then. IIRC, it measures something like 3 feet by 1.5 feet (and about 1.5 feet tall or so). I don't currently have anywhere in my house or shop to put it. Someday, though...

:)

My bad - it was set 40:

http://www.angelfire.com/journal/scottmills/SpaceWarp/set40/set40.html

4x4 base plates (and not quite as big - youthful mis-rememberance!)...

;D

Still too big for anyplace in my house, though!

Did some searching on Ebay - I guess Spacewarp has been re-released (nice); I also saw something that I think is the same thing (seems to be real similar, anyhow) called “Spacerails”…

:slight_smile:

Errr. Can you please stay on topic and handle the spacewarp sets through PM? Thank you.

I was thinking about adding a random function for the delays in the automatic part. Would this be okay?

rand = random(3, 7) * 1000

delay(rand);

Would it be possible for you to stop the train at the station? Maybe with electro magnets, and then also use a few of those to make it move forwards again and go to the chain. Just to make it move more like a real rollercoaster.

We run a chain through the station like the ones used on lifts, we just let the train stop at the brakes in front of the station which are permanent and use the chain to drag it back into the station.

Sorry for the double post.

The model rollercoasters are doing their laps at the funfair exposition. Looping Star the biggest rollercoaster has project Phase implemented and has worked flawlessly so far. The only problems we had were track related problems. Because the chainlift stops at the highest point we had to install a secondary chain-dog underneath the last car. This one would occasionally get stuck on the brake section and other parts with chains. It made the chain on the lift-hill snap once. Yesterday we didn't have any problems.

A trailer full of coaster, not many people can say that.

That's me installing the lights.

And a fully finished coaster.

It took 11 hours to get it to work at the expo. Putting the 5 separate parts from LoopingStar together took a while. We also had to "dig" a gutter for the electronics in the base. It's all nice and hidden.

I'm of for another long day at the expo now. We will try to get a video from it working. It will be hard because it is a very popular model in the tent. Rarely there are no people watching.

Triple Post,

Overdue video. Enjoy: http://www.youtube.com/watch?v=2zoO08M3VTk

The LED Sign (Arduino powered): http://www.youtube.com/watch?v=xwlH_2OQxXU

The Ride Cycle (shows what the Arduino can control): http://www.youtube.com/watch?v=JtuzKLEc7FE

Good project how long did it take you to get it all together?

Nice, your coding is really fancy compared to mine, but mine will work automatically without me pushing a button so there :P

Here's my cable lift .

//cable lift\\

int ledPins [] = {2 ,3} //Motor control 2 left 3 right
int inputPin =5 // switch
int inputPin =6 // switch
int inputPin =7 // switch
int inputPin =8 // switch
void setup (){

pinMode (ledPin [] ,2,3, OUTPUT)
pinMode (inputPin [] 4,5,6,7 INPUT)
}
void loop() {
   //pulls car up hill
    if  digitalRead) (inputPin ,5) == LOW { 
     (digitalWrite)(ledPin ,2 HIGH) ;
   }

   //takes chain down form top
     if (digitalRead) (inputPin ,6) == LOW {
       (digitalWrite) (ledPin ,3 HIGH) ;
     
   }
   //stops chain at the bottome of the lift hill
       if (digitalRead) (inputPin ,7) == LOW {
         (digitalWrite)(ledPin ,3 LOW) ;
         
   }// station walk through
       if (digitalRead) (inputPin ,8) == LOW {
         (digitalWrite)(ledPin ,4 HIGH) ;
         delay (7000)
         (digitalWrite) (ledPin ,4 LOW) ; 

       }

}

I used intLED pin because I'm still not quite sure if i can use motor.. it doesn't matter anyhow it's just gonna get fed into an H-bridge to supply the necessary power for the lift

I may end up using PWM outputs for the motors but we'll see.