RaylFX – Fair Module

RaylFX effects for model railroad and model making with Arduino Nano fairground module title

The first module of the RaylFX series is the Fair module. These are various effects that you can expect to see at a fair:

  • Ferris Wheel
  • Carousel
  • Running light
  • Street lights
  • Stroboscope
  • Sound module

Video – RaylFX Fair Module

We need your consent to load the content of YouTube.

If you click on this video we will play the video, load scripts on your device, store cookies and collect personal data. This enables [Google Ireland Limited, Irland] to track activities on the Internet and to display advertising in a target group-oriented manner. There is a data transfer to the USA, which does not have EU-compliant data protection. You will find further information here.

Jmx0O2RpdiBjbGFzcz0mcXVvdDtudi1pZnJhbWUtZW1iZWQmcXVvdDsmZ3Q7Jmx0O2lmcmFtZSB0aXRsZT0mcXVvdDtSYXlsRlggSmFocm1hcmt0ICZuZGFzaDsgRWZmZWt0ZSBmJnV1bWw7ciBNb2RlbGxiYWhuIHVuZCBNb2RlbGxiYXUgbWl0IEFyZHVpbm8mcXVvdDsgd2lkdGg9JnF1b3Q7MTIwMCZxdW90OyBoZWlnaHQ9JnF1b3Q7Njc1JnF1b3Q7IHNyYz0mcXVvdDtodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9kcGJ2RjV3WHo5ND9mZWF0dXJlPW9lbWJlZCZxdW90OyBmcmFtZWJvcmRlcj0mcXVvdDswJnF1b3Q7IGFsbG93PSZxdW90O2FjY2VsZXJvbWV0ZXI7IGF1dG9wbGF5OyBjbGlwYm9hcmQtd3JpdGU7IGVuY3J5cHRlZC1tZWRpYTsgZ3lyb3Njb3BlOyBwaWN0dXJlLWluLXBpY3R1cmUmcXVvdDsgYWxsb3dmdWxsc2NyZWVuJmd0OyZsdDsvaWZyYW1lJmd0OyZsdDsvZGl2Jmd0Ow==

Components

These are the components I used:

Wiring Diagram

RaylFX fairground carousel wiring diagram with Arduino Nano for model making model railroad

Like all RaylFX modules the fairground module needs connections to the control module (Of these you need only one module in the whole system!). This is a connection of all A4 pins (this pin is used as digital input 18) and a connection to the power supply (5V+ and GND).

Attention: The control signal of the RaylFX control module is applied to pin A4. It controls the time of day and is mandatory to synchronize all modules.

RaylFX effects for model railroad and model making with Arduino Nano fairground module overview

The Ferris wheel and carousel are two stepper motors that drive the models of the respective rides. Both stepper motors are controlled by one stepper motor driver each.

The LEDs for the running lights, the street lights and the strobe are each connected to digital Arduino pins via a series resistor. The resistor limits the current flow. If you need more LEDs you can simply connect them in parallel after the resistor. They will then all shine less brightly. Surely there is a maximum of LEDs you can connect like this. I don’t have an exact number. I recommend to simply try it out.

If you need many more LEDs, you should amplify the channels. This can be achieved e.g. with a ULN2803A Darlington transistor array.

The DFPlayer Mini is used for the sound output. It plays MP3 and WAV files from an SD card. The SD card must be formatted in the FAT16 or FAT32 file system. In addition, the files must be in the folder “01” and the file names must be sequentially named 001.mp3, 002.mp3, 003.mp3, and so on. As mentioned, WAV files may also be used: 001.mp3, 002.wav, 003.mp3 …

Since the stepper motors need a lot of power and therefore can interfere with the DFPlayer, you can put a backup capacitor (100uF) in the power rails near the driver boards.

The operation site module uses the DFRobotDFPlayerMini, and the CheapStepper library. These libraries must be installed first. For this go in the Arduino menu to Sketch>Include Library>Manage Libraries … and search for DFRobotDFPlayerMini in the search field. Install the current version of the DFRobotDFPlayerMini library from DFRobot and then search for, and install the CheapStepper library by Henry Tyler.

Further info about the DF-Player on this page: DFPlayer Mini-MP3-Player for Arduino.

RaylFX effects for model railroads and model making with Arduino Nano Schematic of the Fair module Close-Up

Code Settings

int runningLightTimeout = 100;        // running light speed (ms)
int strobeFlashTimeout = 100;         // strobe impulse duration (ms)
int strobePauseTimeout = 1000;        // strobe pause duration (ms)
int ferrisWheelPauseTimeout = 3;      // ferris wheel pause duration (s)
int carouselPauseTimeout = 2;         // carousel pause duration (s)
int streetLightsTimeout = 100;        // switch-on time of the street lights (ms)
int volumeDFPlayer = 10;                      // volume of the DFPlayer (0 – 30)

int mp3Count = 7;                               // amount of MP3 files on the SD-card
int mp3Duration[] = {28, 9, 20, 2, 4, 17, 23};  // duration of the MP3 files in seconds
int mp3Likelihood = 10;                         // likelihood with which MP3s are played, 10 often, 100 rarely

Different settings can be made in the code:

  • Running light speed in milliseconds
  • Stroboscope pulse duration in milliseconds
  • Stroboscope pause duration in milliseconds
  • Ferris wheel pause duration in seconds
  • Carousel pause duration in seconds
  • Street light on time in milliseconds
  • Volume of the DFPlayer (0 – 30)
  • Amount of MP3 files on the SD card
  • Duration of MP3 files in seconds (can be shorter than MP3s)
  • Likelihood with which MP3s are played: 10 often, 100 rarely

Code for the RaylFX Fair Module

/*
     Rayl-FX Fair Module
     StartHardware.org/en
*/

#include <CheapStepper.h>
#include "SoftwareSerial.h"              // required for the DFPlayer
#include "DFRobotDFPlayerMini.h"         // required for the DFPlayer

/* ***** ***** Settings ***** ***** ***** *****  ***** ***** ***** *****  ***** ***** ***** ***** */

int myAddress = 2;                    // must be unique in the system! Must therefore exist only once.

int runningLightTimeout = 100;        // running light speed (ms)
int strobeFlashTimeout = 100;         // strobe impulse duration (ms)
int strobePauseTimeout = 1000;              // strobe pause duration (ms)
int ferrisWheelPauseTimeout = 3;            // ferris wheel pause duration (s)
int carouselPauseTimeout = 2;               // carousel pause duration (s)
int streetLightsTimeout = 100;              // switch-on time of the street lights (ms)
int volumeDFPlayer = 10;                    // volume of the DFPlayer (0 – 30)

int mp3Count = 7;                               // amount of MP3 files on the SD-card
int mp3Duration[] = {28, 9, 20, 2, 4, 17, 23};  // duration of the MP3 files in seconds
int mp3Likelihood = 10;                         // likelihood with which MP3s are played, 10 often, 100 rarely

/* ***** ***** From here begins the program code, which does not need to be adjusted ***** ***** ***** ***** */

int streetLightsPin = 9;                    // this pin connects to the street lights
int runningLight1Pin = 8;                   // this pin connects to the running light A lamps 2
int runningLight2Pin = 7;                   // this pin connects to the running light A lamps 3
int runningLight3Pin = 6;                   // this pin connects to the running light B lamps 1
int runningLight4Pin = 5;                   // this pin connects to the running light B lamps 2
int strobePin = 4;                          // this pin connects to the stroboscope (white LED?)

//int motorPins1[4] = {10, 11, 12, 13};     // pins for stepper-motor 1
//int motorPins2[4] = {14, 15, 16, 17};     // pins for stepper-motor 2

/* Storage Variables */
int runningLightPosition = 0;               // stores the position of running light A
int streetLightsBrightness = 0;             // stores street light brightness

/* Timer Variables */
long streetLightsTimer = 0;                 // timer street lights
long runningLightTimer = 0;                 // timer running light
long strobeTimer = 0;                       // timer stroboscope
long ferrisWheelTimer = 0;                  // timer ferris wheel
long carouselTimer = 0;                     // timer carousel
long soundTimer = 0;                        // timer DFPlayer
long soundTimeout = 0;                      // stores the playing time of the current MP3
long moveStartTime1 = 0;            // this will save the time (millis()) when we started each new move
long moveStartTime2 = 0;            // this will save the time (millis()) when we started each new move

/* State Variables */
int myState = 3;                            // state main-statemachine, starts at 3, since everything is switched off here
int ferrisWheelState = 0;                   // state ferris wheel
int carouselState = 0;                      // state carousel
int strobeState = 0;                        // state stroboscope
int soundState = 0;                         // state DFPlayer
bool moveClockwise21 = true;
bool moveClockwise22 = true;
int stepsLeft1 = 0;
int stepsLeft2 = 0;

int soundRandom;
int theSound;
int soundPlaying = false;

bool moveClockwise1 = true;
bool moveClockwise2 = true;

String stateName;

/* Variables from the control module to determine the time of day */
boolean receive = false;
boolean receiveStarted = false;
int receiveTimeout = 10;
long receiveTimer = 0;
int receivedTime = 0;
int receivePulse = 0;
int lastReceivePulse = 0;
int receivePin = 18;
int myTime = 0;

/* Create Objects */
SoftwareSerial mySoftwareSerial(3, 2);   // RX, TX for DFPlayer
DFRobotDFPlayerMini myDFPlayer;          // DFPlayer object

CheapStepper stepper1 (14, 15, 16, 17);  // creating a stepper object for the ferris wheel
CheapStepper stepper2 (10, 11, 12, 13);  // creating a stepper object for the carousel

#define PAYLOAD_SIZE 2                   // mandatory for communication with master-module
int timeOfDay = 0;                       // stores timeOfDay of master-module (0 and 255)
byte nodePayload[PAYLOAD_SIZE];          // temporarily stores data of master-module

void setup() {
  Serial.begin(115200);                  // starts the serial communication
  pinMode(receivePin, INPUT);            // receiving pin from control-module
  mySoftwareSerial.begin(9600);          // starts the serial communication for DFPlayer
  pinMode(runningLight1Pin, OUTPUT);        // set running light as output
  pinMode(runningLight2Pin, OUTPUT);        // set running light as output
  pinMode(runningLight3Pin, OUTPUT);        // set running light as output
  pinMode(runningLight4Pin, OUTPUT);        // set running light as output
  pinMode(strobePin, OUTPUT);            // set strobe as output
  runningLightOff();
  strobeOff();
  streetLightsOff();
  stepper1.setRpm(16);
  stepper2.setRpm(12);


  /* DFPlayer Setup */
  Serial.println(F("Initializing DFPlayer ... "));
  if (!myDFPlayer.begin(mySoftwareSerial)) {  // use softwareSerial to communicate with DFPlayer
    Serial.println(F("Error: Check connection to DFPlayer and SD-card"));
    /*while (true) {
      delay(0); // Code to compatible with ESP8266 watch dog.
      }*/
  }
  Serial.println(F("DFPlayer Mini online."));
  myDFPlayer.volume(volumeDFPlayer);       // volume is assigned
}
/*
  void receiveEvent(int bytes) {
  while (Wire.available()) {
    timeOfDay = Wire.read();                // stores received time data in the variable timeOfDay
    if (timeOfDay > 22) {
      stateName = "late evening";
      myState = 0; // late evening
    } else if (timeOfDay > 18) {
      myState = 5; // evening
      stateName = "evening";
    } else if (timeOfDay > 12) {
      myState = 4; // noon
      stateName = "noon";
    } else if (timeOfDay > 9) {
      myState = 3; // forenoon
      stateName = "forenoon";
    } else if (timeOfDay > 7) {
      myState = 2;  // morning
      stateName = "morning";
    } else {
      myState = 1;  // night
      stateName = "night";
    }

    Serial.print(millis()); Serial.print("\t timeOfDay: \t"); Serial.print(timeOfDay);
    Serial.print("\t myState: \t"); Serial.print(myState); Serial.print(" — "); Serial.println(stateName);
  }
  }*/

void loop() {

  receiveFunction();                  // execute instructions for reception
  if (receiveStarted == false) {
    if (myTime > 22) {                // ***** late evening *****
      streetLightsOn();               // street lights on
      runningLightOn();               // running light on
      strobeOn();                     // strobe on
      soundOn();                      // sound on
      ferrisWheelMotorOn();           // ferris wheel motor on
      carouselMotorOn();              // carousel motor on
      
    } else if (myTime > 18) {         // ***** evening *****
      streetLightsOn();               // street lights on
      runningLightOn();               // running light on
      strobeOn();                     // stroboscope on
      soundOn();                      // sound on
      ferrisWheelMotorOn();           // ferris wheel motor on
      carouselMotorOn();              // carousel motor on
      
    } else if (myTime > 12) {         // ***** noon *****
      streetLightsOff();              // street lights off
      runningLightOff();              // running light off
      strobeOff();                    // stroboscope off
      soundOff();                     // sound off
      ferrisWheelMotorOn();           // ferris wheel motor on
      carouselMotorOff();             // carousel motor off

    } else if (myTime > 9) {          // ***** forenoon *****
      streetLightsOff();              // street lights off
      runningLightOff();              // running light off
      strobeOff();                    // stroboscope off
      soundOff();                     // sound off
      ferrisWheelMotorOff();          // ferris wheel motor off
      carouselMotorOff();             // carousel motor off

    } else if (myTime > 7) {          // ***** morning *****
      streetLightsOn();               // street lights on
      runningLightOff();              // running light off
      strobeOff();                    // stroboscope off
      soundOff();                     // sound off
      ferrisWheelMotorOff();          // ferris wheel motor off
      carouselMotorOff();             // carousel motor off

    } else {                          // ***** night *****
      streetLightsOn();               // street lights on
      runningLightOff();              // running light off
      strobeOff();                    // stroboscope off
      soundOff();                     // sound off
      ferrisWheelMotorOff();          // ferris wheel motor off
      carouselMotorOff();             // carousel motor off
    }
  }
}

void streetLightsOn() {
  if (streetLightsTimer + streetLightsTimeout < millis()) {
    streetLightsBrightness++;
    if (streetLightsBrightness > 255) streetLightsBrightness = 255;
    streetLightsTimer = millis();
    analogWrite(streetLightsPin, streetLightsBrightness);
  }
}

void streetLightsOff() {
  streetLightsBrightness = 0;
  analogWrite(streetLightsPin, 0);
  streetLightsTimer = millis();
}

void runningLightOn() {
  if (runningLightTimer + runningLightTimeout < millis()) {
    runningLightTimer = millis();
    runningLightPosition++;
    if (runningLightPosition > 3) {
      runningLightPosition = 0;
    }
    if (runningLightPosition == 0) {
      digitalWrite(runningLight1Pin, HIGH);
      digitalWrite(runningLight2Pin, HIGH);
      digitalWrite(runningLight3Pin, HIGH);
      digitalWrite(runningLight4Pin, LOW);
    } else if (runningLightPosition == 1) {
      digitalWrite(runningLight1Pin, HIGH);
      digitalWrite(runningLight2Pin, HIGH);
      digitalWrite(runningLight3Pin, LOW);
      digitalWrite(runningLight4Pin, HIGH);
    } else if (runningLightPosition == 2) {
      digitalWrite(runningLight1Pin, HIGH);
      digitalWrite(runningLight2Pin, LOW);
      digitalWrite(runningLight3Pin, HIGH);
      digitalWrite(runningLight4Pin, HIGH);
    } else if (runningLightPosition == 3) {
      digitalWrite(runningLight1Pin, LOW);
      digitalWrite(runningLight2Pin, HIGH);
      digitalWrite(runningLight3Pin, HIGH);
      digitalWrite(runningLight4Pin, HIGH);
    }
  }
}

void runningLightOff() {
  runningLightTimer = millis();
  digitalWrite(runningLight1Pin, LOW);
  digitalWrite(runningLight2Pin, LOW);
  digitalWrite(runningLight3Pin, LOW);
  digitalWrite(runningLight4Pin, LOW);
}

void strobeOn() {
  switch (strobeState) {
    case 0:
      digitalWrite(strobePin, HIGH);
      if (strobeTimer + strobeFlashTimeout < millis()) {
        strobeTimer = millis();
        strobeState = 1;
      }
      break;
    case 1:
      digitalWrite(strobePin, LOW);
      if (strobeTimer + strobePauseTimeout < millis()) {
        strobeState = 0;
        strobeTimer = millis();
      }
      break;
  }
}

void strobeOff() {
  digitalWrite(strobePin, LOW);
  strobeTimer = millis();
}

void ferrisWheelMotorOn() {
  stepper1.run();

  switch (ferrisWheelState) {
    case 0: // fahren
      stepsLeft1 = stepper1.getStepsLeft();
      if (stepsLeft1 == 0) {
        moveClockwise1 = !moveClockwise1;
        if (random(3) <= 1) {
          ferrisWheelState = 1;
          ferrisWheelTimer = millis();
        } else {
          stepper1.newMoveDegrees (moveClockwise1, 360 + random(360));
        }
      }
      break;
    case 1: // pause
      if (ferrisWheelTimer + (ferrisWheelPauseTimeout * 1000) < millis()) {
        ferrisWheelState = 0;
      }
      break;
  }
}

void ferrisWheelMotorOff() {
  /*stepsLeft1 = stepper1.getStepsLeft();
    if (stepsLeft1>0) stepper1.stop();*/
}

void carouselMotorOn() {
  stepper2.run();
  switch (carouselState) {
    case 0: // spin
      stepsLeft2 = stepper2.getStepsLeft();
      if (stepsLeft2 == 0) {
        moveClockwise2 = !moveClockwise2;
        if (random(5) <= 1) {
          carouselState = 1;
          carouselTimer = millis();
        } else {
          stepper2.newMoveDegrees (moveClockwise2, 90 + random(360));
        }
      }
      break;
    case 1: // pause
      if (carouselTimer + (carouselPauseTimeout * 1000) < millis()) {
        carouselState = 0;
      }
      break;
  }
}

void carouselMotorOff() {
  /*stepsLeft2 = stepper2.getStepsLeft();
    if (stepsLeft2>0) stepper2.stop();*/
}

void soundOn() {
  switch (soundState) {
    case 0:
      Serial.println("soundTimer 0");
      soundRandom = random(mp3Likelihood);
      if (soundRandom < 1) {
        soundState = 1;
        theSound = random(mp3Count) + 1;
        soundTimer = millis();
        soundTimeout = mp3Duration[theSound - 1] * 1000;
        Serial.print("Playsound: \t\t\t"); Serial.print(theSound); Serial.print("\t\t\t"); Serial.println(soundTimeout);
        myDFPlayer.playFolder(1, theSound); //play specific mp3 in SD:/15/004.mp3; Folder Name(1~99); File Name(1~255)
        soundPlaying = true;
      } else {
        soundTimer = millis();
        soundTimeout = 500;
        soundState = 1;
      }
      break;
    case 1:
      if (soundTimer + soundTimeout < millis()) {
        soundPlaying = false;
        soundState = 0;
      }
      break;
  }
}


void soundOff() {
  if (soundPlaying == true) {
    Serial.println(soundPlaying);
    myDFPlayer.pause();
    soundPlaying = false;
  }
}


void printDetail(uint8_t type, int value) {     // DF Player errors
  switch (type) {
    case TimeOut:
      Serial.println(F("Time Out!"));
      break;
    case WrongStack:
      Serial.println(F("Stack Wrong!"));
      break;
    case DFPlayerCardInserted:
      Serial.println(F("Card Inserted!"));
      break;
    case DFPlayerCardRemoved:
      Serial.println(F("Card Removed!"));
      break;
    case DFPlayerCardOnline:
      Serial.println(F("Card Online!"));
      break;
    case DFPlayerUSBInserted:
      Serial.println("USB Inserted!");
      break;
    case DFPlayerUSBRemoved:
      Serial.println("USB Removed!");
      break;
    case DFPlayerPlayFinished:
      Serial.print(F("Number:"));
      Serial.print(value);
      Serial.println(F(" Play Finished!"));
      break;
    case DFPlayerError:
      Serial.print(F("DFPlayerError:"));
      switch (value) {
        case Busy:
          Serial.println(F("Card not found"));
          break;
        case Sleeping:
          Serial.println(F("Sleeping"));
          break;
        case SerialWrongStack:
          Serial.println(F("Get Wrong Stack"));
          break;
        case CheckSumNotMatch:
          Serial.println(F("Check Sum Not Match"));
          break;
        case FileIndexOut:
          Serial.println(F("File Index Out of Bound"));
          break;
        case FileMismatch:
          Serial.println(F("Cannot Find File"));
          break;
        case Advertise:
          Serial.println(F("In Advertise"));
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}


void receiveFunction() {                      // receives time of day from control-module
  receivePulse = digitalRead(receivePin);     // read out the receive pin
  
  if ((receiveTimer + receiveTimeout < millis()) && (receiveStarted == true)) {
    // on timeout and active reception
    receiveStarted = false;                   // end active reception
    myTime = receivedTime - 1;                // store received time
    receivedTime = 0;                         // reset the auxiliary variable for time reception
    Serial.println(myTime);                   // serial output
  }
  // if a pulse is detected at the receive pin that was not there before
  if ((receivePulse == 0) && (lastReceivePulse == 1)) {
    receiveTimer = millis();                  // restart timer
    if (receiveStarted == false) receiveStarted = true;  // start active reception, if not already done
    receivedTime++;                           // there was a pulse, so increase the auxiliary variable to receive time
  }
  lastReceivePulse = receivePulse;            // remember current state at pin for next pass
}

1 thought on “RaylFX – Fair Module”

  1. Pingback: DFPlayer Mini MP3 Player for Arduino - StartHardware - Tutorials for Arduino

Leave a Reply

Your email address will not be published.