Animated Spaceport Diorama with Traveller RPG spaceship

Animated Spaceport Diorama with Traveller RPG spaceship

This is a fully animated spaceport diorama with working cargo gantry, monorail, light up control panels, landing pad lights and flashing warning lights plus a siren when a ship is due to land. The spaceship is from a crowdfunding campaign currently running on MyMiniFactory for 2nd Dynasty. It is a Type S Scout/Courier from the Traveller RPG and is a classic design dating back to the seventies.

MyMiniFactory approached me to build a diorama to support their latest crowdfunding campaign and, knowing what I like to build, they suggested the Traveller spaceship. I checked out the designer, 2nd Dynasty, and realised that I had seen his designs many times and always been really impressed by them, especially as they are original. This campaign is based on the role playing game the started back in 1979. Full disclosure, I haven’t played Traveller but many people I have spoken to have and this is one of the iconic ships.

I set about designing a diorama to go around it and chose to print at 15mm (around 1/120) rather than the expected 28mm. This meant that I could build a smaller diorama but still put the spaceship in it at an appropriate size.

If you want to get a hold of the ship then check out the crowdfunding on MyMiniFactory running until 2nd August 2021:


The Amazon links are all items I have either bought or bought something similar for myself. Clicking on any links to Amazon will give me a small affiliate income which I use to produce more videos at absolutely no extra cost to yourself. Every little helps!

3D Print Files

The spaceship is the Type S but I combined the external STLs and printed as one piece and in smaller scale:
I printed many of the Type S interior parts as clutter and also the campaign power suits which are add-ons
I also 3D printed the following sets from 2nd Dynasty that you can buy on MyMiniFactory:
Vacuum Loader Mech Pack
Cargo Pack which includes the bigger loaders

I used some repulsor barges from Imperial Terrain to hold the Type S engines.


I used stepper motors from an old Anycubic i3 Mega plus the limit switches

The rest was arduino based and the code is below if you are interested. It’s powered by a 12v 5A transformer.
Stepper Driver Controllers – I used this to guide me: You can buy TB6600 controllers here:
To convert the 12V to 3.3V for the LEDs, I used this convertor:
The LEDs were 3mm and 5mm from my stash but I bought some flashing red LEDs:


I used what was on hand so mostly a mix of Tamiya and Vallejo colours plus artists’ acrylics such as Heavy Body White
Tamiya fine grey surface primer
Tamiya fine white surface primer
I do use Motip Spray Putty on all FDM 3D printed parts
Halfords Automotive spray paint in matt black, gloss black and oxide red
Rustoleum Desert Bisque on the monorail and base
Rustoleum Aged Iron for the tarmac


Black Foam 30mm
I buy my styrene from Slaters Plastikard
Pattern Notcher
Tamiya Scriber


Taupe Tile Grout by Larsen
Wet Water – Isopropyl Alcohol and Water mix (1:2)
Mig Productions City Industrial Dirt Pigment This has been out of production for a long time so try Ammo by Mig instead.
Factory Grey
Mig Productions Dark Enamel Wash was out of production for years so try Ammo by Mig Panel Lines Wash instead.
Mig Productions Dirty Glass Enamel Wash – It’s a blue/green grey and Ammo by Mig will have something similar.

Arduino Code

       /* Need following:
 *  Landing pad lights - simple on/off switch.
 *  Building Lights - would be nice to have red flashing light on antenna but otherwise just on/off
 *  Warning Lights - need to flash and have siren sound
 *  Control Tower - simple on/off switch.
 *  Monorail - needs stepper motor
 *  Cargo Gantry - needs stepper motor
// Steppers
#include <AccelStepper.h>
// pin assignments

const int STEP_PIN1 = 8;  // Monorail
const int DIRECTION_PIN1 = 9;  // Monorail
//const int ENABLE_PIN1 = ;  // Monorail
const byte LIMIT_SWITCH_PIN_1L = 2;  // Monorail Left
const byte LIMIT_SWITCH_PIN_1R = 3;  // Monorail Right
const int STEP_PIN2 = 10;  // Cargo Gantry
const int DIRECTION_PIN2 = 11;  // Cargo Gantry
//const int ENABLE_PIN2 = ;  // Cargo Gantry
const byte LIMIT_SWITCH_PIN_2L = 4;  // Cargo Gantry Left
const byte LIMIT_SWITCH_PIN_2R = 5;  // Cargo Gantry Right

// Define some steppers and the pins the will use
AccelStepper stepper1(AccelStepper::DRIVER, STEP_PIN1, DIRECTION_PIN1);  // or  AccelStepper stepper(1, 8, 9);
AccelStepper stepper2(AccelStepper::DRIVER, STEP_PIN2, DIRECTION_PIN2);  // or  AccelStepper stepper(1, 10, 11);
bool switchFlipped = false; //stores the status for flipping
bool previousFlip = true; //stores the previous state for flipping - needed for the direction change
// Warning Lights
// Variables will change:
int ledState = LOW;             // ledState used to set the LED
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated
// constants won't change:
const long interval = 500;           // interval at which to blink (milliseconds)
int  lowerFreq       = 440;
int  higherFreq      = 1000;
int  deltaFreq       =   2;
int  toneDelay       =  20;
int  toneDelayOffset =  -1;
long lastBuzzTime    =   0;
int  actFreqStep   = deltaFreq;
int  actFreq       = lowerFreq;

// Warning Light and Siren pins
int buzzerPin =  6;   // Siren  
const int LEDPin   = 7;  //Warning Lights 
//Push Buttons
  const int Warn_Pin   = 1;  //Warning Lights 
  const int Mono_Pin = 12; //Monorail  
  const int Carg_Pin = 13; //Cargo Gantry
  int Warn_buttonState = 0;         //Warning Lights variable for reading the pushbutton status
  int Mono_buttonState = 0;         //Monorail variable for reading the pushbutton status
  int Carg_buttonState = 0;         //Cargo Gantry variable for reading the pushbutton status

void setup()
// Steppers
 //Limit Switches
  pinMode(LIMIT_SWITCH_PIN_1L, INPUT_PULLUP); // internal pullup resistor (debouncing)
  pinMode(LIMIT_SWITCH_PIN_1R, INPUT_PULLUP); // internal pullup resistor (debouncing)
  pinMode(LIMIT_SWITCH_PIN_2L, INPUT_PULLUP); // internal pullup resistor (debouncing)
  pinMode(LIMIT_SWITCH_PIN_2R, INPUT_PULLUP); // internal pullup resistor (debouncing)

    //Stepper parameters
//    stepper1.setEnablePin(ENABLE_PIN1);
//  stepper1.setPinsInverted(false, false, true);  //If it doesn't run, try this
    stepper1.setMaxSpeed(5000);  //SPEED = Steps / second  
    stepper1.setAcceleration(1000);  //ACCELERATION = Steps /(second)^2   
//    stepper2.setEnablePin(ENABLE_PIN2);
//   stepper2.setPinsInverted(false, false, true);  //If it doesn't run, try this
    stepper2.setMaxSpeed(5000);  //SPEED = Steps / second  
    stepper2.setAcceleration(1000);  //ACCELERATION = Steps /(second)^2   
// Warning Lights
 // set the digital pin as output:
  pinMode( LEDPin, OUTPUT ) ;
  // initialize LEDs off 
  ledsOff( );
  // Siren
  // buzzer to the act frequency
  buzzer( actFreq );
  // initialize the button pins as an input:
  pinMode(Warn_Pin, INPUT_PULLUP);
  pinMode(Mono_Pin, INPUT_PULLUP);
  pinMode(Carg_Pin, INPUT_PULLUP);  


void loop()

  // read the state of the button value:
  Warn_buttonState = digitalRead(Warn_Pin);
  Mono_buttonState = digitalRead(Mono_Pin);
  Carg_buttonState = digitalRead(Carg_Pin);
// Steppers
  // check if the button is pressed. If it is, the buttonState is HIGH:
 if (Mono_buttonState == HIGH) {
      stepper1.runSpeed(); //step the motor (this will step the motor by 1 step at each loop indefinitely)
  if (Carg_buttonState == HIGH) {
  stepper2.runSpeed(); //step the motor (this will step the motor by 1 step at each loop indefinitely)
  flipCheck();   //checking the flip at each loop

  // Warning Lights
  if (Warn_buttonState == HIGH) {
  } else {
  //Additional Loops
  // Siren
void buzzer( int freq ) 
  tone( buzzerPin, freq );
void buzzeroff() 
  noTone( buzzerPin );

  //Warning Lights
void ledsOff()
  digitalWrite( LEDPin, LOW );
  //Warning Lights
void WarningLights()
   // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;
    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    // set the LED with the ledState of the variable:
    digitalWrite(LEDPin, ledState);
  // Siren
void Siren()
  if ( ( millis() - lastBuzzTime ) > ( toneDelay + toneDelayOffset ) )
    actFreq += actFreqStep;
    if ( actFreq >= higherFreq )
      actFreqStep   = -1 * deltaFreq;
      // down sweep is slower 
      toneDelayOffset = -1;
    if ( actFreq <= lowerFreq )
      actFreqStep   =  deltaFreq;
      toneDelayOffset =  0;
    buzzer( actFreq );
    lastBuzzTime = millis();

// Steppers
void flipCheck() //this function constantly polls the pins. It is not the most efficient practice...
  if(digitalRead(LIMIT_SWITCH_PIN_1L) == LOW)
  if(digitalRead(LIMIT_SWITCH_PIN_1R) == LOW)
    if(digitalRead(LIMIT_SWITCH_PIN_2L) == LOW)
  if(digitalRead(LIMIT_SWITCH_PIN_2R) == LOW)

Final Photos

If you enjoy this then please sign up to my newsletter for the next instalment.

We are a participant in the Amazon Services LLC Associates Program and the Amazon EU Associates Programme, affiliate advertising programs designed to provide a means for us to earn fees by linking to, and affiliated sites.