Concept

I try to find a new challenge in every project. For the Evil Queen’s Dungeon book nook, I wanted to pay homage to the Disneyland Snow White’s Scary Adventures ride before it was plussed to make it a bit less terrifying to small children. For this book nook, the three challenges were:

  • Use an audio sound track that can be switched on and off
  • Have lightning flashes synchronized to the audio
  • Use UV LEDs and fluorescent paint

Successes and challenges

I am extremely happy… that this book nook is DONE! It turned out well and I checked all of the boxes on the challenges. It fought me every step of the way. Fluorescent paint SUCKS! It’s not the paint’s fault—I used really good paint. It just shows every stroke and flaw under UV and you need to paint under UV which I’m sure caused me permanent damage of some sort… ok… not really. It’s a tough material to use and I found that airbrushing always provided the best results so… TIP! There are also a lot of little bits and bottles. Fighting with the paint made wanting to paint a huge chore… which it shouldn’t be.

Resources

It’s important to respect creative rights so I will link to source files where appropriate and provide downloads to files that I’ve created or where licensing allows me to freely redistribute. Sharing is caring in the maker community but, as creative people, we all would like credit where credit is due.

Sound

I have a monthly subscription to Epidemic Sound. I do not currently receive any fat envelopes of cash to endorse them and there are several other free and paid sources of sound tracks and effects out there. For this project, I was inspired by the Sounds of Disneyland audio site and used their Dark Rides section as an inspiration to build my soundtrack.

Downloads

Links

Code

				
					/*
  Nothing happens in a vacuum. Thanks to the following:

  Adafruit NeoPixel Uberguide
  https://learn.adafruit.com/adafruit-neopixel-uberguide
  DFPlayer Mini Docs
  https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299

  "If I have seen further it is by standing on the shoulders of Giants."
  - Isaac Newton
*/
// These libraries must be installed prior to uploading
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
// neopixel
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
// Multitasking setup
long previousMillis01 = 0;
long previousMillis02 = 0;
long previousMillis03 = 0;
long previousMillis04 = 0;

// Audio setup
// Use pins 2 and 3 to communicate with DFPlayer Mini
static const uint8_t PIN_MP3_TX = 2; // Connects to module's RX
static const uint8_t PIN_MP3_RX = 3; // Connects to module's TX
SoftwareSerial softwareSerial(PIN_MP3_RX, PIN_MP3_TX);
DFRobotDFPlayerMini player;
// Define the audio play button pin
int audiopin = 8;
long startMillis = 0;

// Set initial brightness (0-255)
int brightness = 255;
int flickerDelay = random(10, 100);
int flashDelay = random(30, 100);
int cauldronDelay = random(10, 100);
int pulseDelay = 50;
int r = 0;
int g = 0;
int b = 0;
int w = 0;
int state = 0;
int inc = 0;


// Fire and Cauldron
Adafruit_NeoPixel strip01 = Adafruit_NeoPixel(2, 4, NEO_GRB + NEO_KHZ800);
// Cell
Adafruit_NeoPixel strip02 = Adafruit_NeoPixel(1, 5, NEO_GRB + NEO_KHZ800);
// Windows
Adafruit_NeoPixel strip03 = Adafruit_NeoPixel(2, 6, NEO_GRBW + NEO_KHZ800);
// Ceiling
Adafruit_NeoPixel strip04 = Adafruit_NeoPixel(5, 7, NEO_GRB + NEO_KHZ800);

// Define color of flicker effect function
// c = color being randomize
// r = random range - the smaller the number the less variation
int myFlickerColor(int c, int r) {
  int flicker = random(0, r);
  c = c - flicker;
  if (c < 0) c = 0;
  return c;
}
int myFlickerColor02(int c, int r) {
  int flicker = random(0, r);
  c = c - flicker;
  if (c < 0) c = 0;
  return c;
}

void setup() {
  // Audio setup
  // Init serial port
  Serial.begin(9600);
  // Init serial port for DFPlayer Mini
  softwareSerial.begin(9600);
  // Start communication with DFPlayer Mini
  if (player.begin(softwareSerial)) {
    //Serial.println("OK");
    player.EQ(DFPLAYER_EQ_JAZZ);
    player.volume(0);
  } else {
    //Serial.println("Connecting to DFPlayer Mini failed!");
  }
  pinMode(audiopin, INPUT_PULLUP);
  // Neopixel setup
  strip01.begin();
  strip02.begin();
  strip03.begin();
  strip04.begin();
  // Set solid color LEDs
  // Parameters (LED number, Red, Green, Blue), LED number starts with 0, color values 0-255
  // Cell
  strip02.setPixelColor(0, 100, 200, 50);
  strip02.show();
  // Ceiling
  // Rear left
  strip04.setPixelColor(0, 25, 100, 10);
  // Front left
  strip04.setPixelColor(1, 75, 0, 50);
  // Front center
  strip04.setPixelColor(2, 5, 30, 5);
  // Front right
  strip04.setPixelColor(3, 75, 25, 0);
  // Rear right
  strip04.setPixelColor(4, 85, 0, 50);
  strip04.show();

  // Randomize random
  randomSeed(analogRead(0));
}

void loop() {
  //grab current milliseconds
  unsigned long currentMillis = millis();
  // check for audio on
  if (digitalRead(audiopin) == HIGH) {
    player.pause();
  } else
  {
    // player.volume(20);
  }
  // check to see if audio is playing
  int busyPin = 0;
  busyPin = analogRead(A0);
  // Test value of busy pin and button state
  // Trigger audio if busy pin reads over 300 (no audio playing) and button is high
  if (busyPin > 300 && digitalRead(audiopin) != HIGH) {
    player.volume(20);
    player.play(1);
    startMillis = currentMillis;
  }
  // NeoPixel LEDs
  // Windows
  if (currentMillis - startMillis < 3000) {
    if (currentMillis - previousMillis01 > flashDelay) {
      previousMillis01 = currentMillis;
      w = myFlickerColor02(220, 220);
      strip03.setPixelColor(0, 10, 10, 0, w);
      strip03.setPixelColor(1, 10, 10, 0, w);
      strip03.show();
      flashDelay = random(50, 200);
    }
  } else {
    strip03.setPixelColor(0, 120, 200, 0, 0);
    strip03.setPixelColor(1, 120, 200, 0, 0);
    strip03.show();
  }
  if (currentMillis - previousMillis04 > pulseDelay) {
    previousMillis04 = currentMillis;
    if (state == 0 and inc < 75)
    {
      strip01.setPixelColor(1, inc, 25, inc);
      strip01.show();
      inc++;
    } else {
      state = 1;
    }
    if (state == 1 and inc > 0) {
      strip01.setPixelColor(1, inc, 25, inc);
      strip01.show();
      inc--;
    } else {
      state = 0;
    }
  }
  if (currentMillis - previousMillis03 > flickerDelay) {
    previousMillis03 = currentMillis;
    // Define fire color
    r = myFlickerColor(60, 30);
    g = myFlickerColor(20, 10);
    b = myFlickerColor(0, 10);
    strip01.setPixelColor(0, r, g, b);
    strip01.show();
    flickerDelay = random(25, 150);
  }
}
				
			

Final thoughts

This was a grueling project that really shouldn’t have been. As book nooks go, there wasn’t anything particularly tricky about it! What really sapped my motivation was the constant struggle with the UV paints. Most of this comes down to my obsessiveness and I could have easily chalked it up to a learning experience and moved on. Disney Dark Rides are all about glow-in-the-dark though and, when it works, UV paint is a slick look and can add a lot of impact to a book nook.