Category Archives: Halloween

Hacking Home Depot Lethal Lily animatronic – part 3

See Also: intro, part 1, part 2, part 3 and part 4. (Github project.)

So far, I have figured out that the Lethal Lily Control Box sends out serial bytes at around 550 baud (on the green wire), with a byte sent at the start of a sequence, and a different byte sent at the end.

In between these two serial bytes, a second (blue) wire is pulsing at different rates. This looks like PWM pulses to control the position of servos. (Me from the future: It is not…) The question is … how does one stream of dozens of pulses end up at the different servos in the head? (Me from the future: It does not…)

To figure that out, it’s time to experiment — and hopefully not destroy the servos in the head at the process! (Many devices have no protection so you can tell the servo to go so far it breaks the mechanism. Without knowing how the internals of the head operate, I risk ruining this thing if I send it the wrong kind of pulse.)

Instead of just trying to send pulses and see what happens, I decided I might like to capture the pulses that come from the Control Box, and try to recreate them on the Arduino and see if I get the same result.

5.9V Warning

The power supply used by the Control Board is 5.9V. The Arduino UNO I/O pins are 5V pins, so connecting higher voltage to them may cause damage. I decided to enable the Analog channel in my Saleae Logic Analyzer capture and see what voltage is coming out of the blue wire.

This tells me that the pulses are 5.4V — a bit lower than the 5.9V power supply, but still beyond the voltage that should go in to an I/O pin.

I consulted ChatGPT to ask how I could drop the voltage down to sub-5V for the Arduino.

I know these A.I. tools can spew bad information, but if this is correct, I should be able to use two resistors and drop the power down to a safe level for the Arduino to read.

Here is a rough look at how I tried to wire that up.

Arduino voltage divider to take 5.4V down to below 5V.

Hardware folks, quit laughing. This is complicated for a software guy ;-)

I use PIN 2 for reasons I will explain in just a moment.

I did skip some of the steps it took me to get here. Initially, ChatGPT gave me different resistor values. I did not have resistors of the proper values available, and since Radio Shack doesn’t exist anymore, I asked if there were ways to do it with other values. It made a table for me, and I was able to use two resistors I had on-hand. The A.I. was quite helpful in showing options until I got one that worked for what I had.

Counting Pulses

By asking ChatGPT, I was able to make some super simple code that could count how many pulses were coming from the Control Box. But, I wanted to do more than that. I wanted to know how long each pulse was on, and off in between. To do this, I needed to record the time when the pulse changed state (off to on, or on to off) and then later I could do math on those values to figure out how long each pulse was on or off.

If my program were doing nothing but this, I might be able to brute-force it by just reading the pin in a loop. If it changed state (low to high, or high to low), get the current time. If there was a previous time (previous change), print how long it’s been since the last change.

#define BLUE_WIRE_PIN   2   // Pulses IN

void setup() {
    Serial.begin(9600);
    while (!Serial);

    pinMode(BLUE_WIRE_PIN, INPUT);

    Serial.println ("Running...");
}

void loop() {
  unsigned long time = 0;
  static unsigned long s_prevTime = 0;

  int pinStatus = 0;
  static int s_lastPinStatus = -1;

  pinStatus = digitalRead (BLUE_WIRE_PIN);

  if (pinStatus != s_lastPinStatus)
  {
    // Pin changed. Record the time.
    time = millis();

    if (s_prevTime != 0)
    {
      // Print how much time since last change.
      Serial.println (time - s_prevTime);
    }

    s_prevTime = time;
    s_lastPinStatus = pinStatus;
  }
}

When I run this, then trigger the device, I see a series of numbers print out representing the pulse (on or off) times.

I found that if you switch the Control Box to “OFF – TRY ME” and then back to “SENSOR“, it will reset to pattern 1. I did this so I could capture the pulses for pattern 1.

But, when I compare these times with the times of the pulses seen by the Saleae, they are very different. For example, the first on pulse reported by the Arduino script is 108 ms, but the Saleae thinks it is 145ms.

This is caused by the pulse signal having some decay after it turns “off”. Rather than being a sharp ON/OFF square pulse, it stays on, then slopes down at the end. The Saleae detects the “end” at a different point in this slope than the Arduino does.

You can see there is about a 35ms delay from the time the signal starts sloping off until the Saleae sees it as “off”. The Arduino sees “off” pretty much right when the signal starts to drop. (108 ms detected by Arduino plus 35 ms is 143 ms, very close to what the Arduino is calculating). I suspect if this is important to the head, we can easily compensate in software by adding to the values the Arduino calculates.

But we’re not there yet.

Suffice it to say, it does seem easy to capture the pulses and record how long each is, which would allow us to play them back to the head later, with the Arduino generating the same pulse sequence that the Control Box did.

Using Arduino Interrupts

When I first did this test, I thought the reason I was getting different numbers was caused by the code doing other things before it got back to check the pin. The pin cand change while the code was elsewhere in the loop, and by the time we get back to read the pin, it’s a bit later than when the change actually occurred.

To compensate for this, I learned how to use Arduino Interrupts. Some pins can generate an interrupt when they change, and you can configure Arduino to run a routine any time this change happens. On the Arduino UNO, only pins 2 and 3 can generate interrupts. This is why I chose PIN 2 for my example.

I updated my example to use an interrupt service routine (ISR) instead of manually polling the pin:

#define BLUE_WIRE_PIN   2   // Pulses IN

void setup() {
    Serial.begin(9600);
    while (!Serial);

    pinMode(BLUE_WIRE_PIN, INPUT);
    
    attachInterrupt(digitalPinToInterrupt(BLUE_WIRE_PIN), pinChangedISR, CHANGE);

    Serial.println ("Running...");
}

void loop() {
}

// This will be called any time the pin changes state.
void pinChangedISR()
{
    unsigned long time = 0;
    static unsigned long s_prevTime = 0;
  
    // Pin changed. Record the time.
    time = millis();

    if (s_prevTime != 0)
    {
      // Print how much time since last change.
      Serial.println (time - s_prevTime);
    }

    s_prevTime = time;
}

In this version, the main loop() is now empty. All the work is being done inside the ISR. This is normally not how you would use an interrupt since, while processing the code inside the ISR, any more interrupts may be missed. The ISR code needs to be as small and quick as possible. It works for this example because the timing of the pulses is quite slow (pulses lasting many milliseconds) but this is still not a good example of an ISR.

When I run this, I see similar numbers. This tells me the difference in Arduino vs Saleae values was not the code being too slow. I also saw some 0 values. This told me the Arduino might be seeing glitches on the line that were not full pulses. Some “debounce” code could be added to fix this, but that’s outside the scope of this quick test.

During this test, I also learned that the millis() (millisecond time) counter on the UNO incrementis at a value of 1.2 each time, then would jump ahead every so often to catch up. This might explain some of the drift in numbers I see when re-running this. To make the timing more accurate, I will switch to using micros() (microseconds) which will be as precise of timing available on the Arduino. More on that later…

Now that I have a basic understanding of how I can read the green wire serial data, and the blue wire pulse data, I came up with a test program that displays both. Here is my LethalLilyReader test program that displays the serial bytes, then the pulse information, for each pattern as it is triggered. The pulse data is collected in the ISR, but not printed until the pattern is done, which makes the interrupt service routine smaller and faster.

I also made it count the number of pulses and display that, so I could further compare what the Arduino sees to the Saleae.

And as a final feature, I fudge the timing numbers to compensate for the sloping voltage delay the Control Box sends at the end of a pulse. This is not perfect, but it gets the numbers pretty close to how Saleae sees them.

Here is the full program (but check my Github for any changes since this article was posted):

// LethalLilyReader.ino
/*-----------------------------------------------------------------------------
 
 Lethal Lily Swamp Witch animatronic Control Box reader.
 By Allen C. Huffman (alsplace@pobox.com)
 www.subethasoftware.com
 
 This program reads the data come from the Lethal Lily Control Box over a
 serial and digital input.
 
 Hardware:
 
 https://sviservice.com/collections/2023/products/sv2323794
 
 Documentation:
 
 CONFIGURATION:
 1. Define the pins on the Arduino that will be used for RX in
 .  the Software Serial library (connected to the green wire), and the pulse
 .  counting pin (connected to the blue wire with 10K/100K resistors to drop
 .  the voltage from 5.4V down to below 5V).
 
 VERSION HISTORY:
 2024-06-12 0.00 allenh - Created basic serial (green wire) reader.
 2014-06-13 0.01 allenh - Adding pulse (blue wire) support.
 
 TODO:
 * Capture data and then playback to the head.
 
 TOFIX:
 * TODO...

 EXAMPLE OUTPUT:

[0xE1 - 11100001] PLAYING #1 ... [0x78 - 1111000] STOPPED.
Pulses: 65
[0xD2 - 11010010] PLAYING #2 ... [0x78 - 1111000] STOPPED.
Pulses: 71
[0xC3 - 11000011] PLAYING #3 ... [0x78 - 1111000] STOPPED.
Pulses: 59
[0xB4 - 10110100] PLAYING #4 ... [0x78 - 1111000] STOPPED.
Pulses: 68
[0xA5 - 10100101] PLAYING #5 ... [0x78 - 1111000] STOPPED.
Pulses: 75
[0x96 - 10010110] PLAYING #6 ... [0x78 - 1111000] STOPPED.
Pulses: 74
[0x87 - 10000111] PLAYING #7 ... [0x78 - 1111000] STOPPED.
Pulses: 57
[0xE1 - 11100001] PLAYING #1 ... [0x78 - 1111000] STOPPED.
Pulses: 65
----------------------------------------------------------------------------*/ 

/*--------------------------------------------------------------------------*/
// Configuration
/*--------------------------------------------------------------------------*/
#define BAUD_RATE       550
#define GREEN_WIRE_PIN  12  // Serial RX
#define BLUE_WIRE_PIN   2   // Pulses IN

/*--------------------------------------------------------------------------*/
// Includes
/*--------------------------------------------------------------------------*/
#include <SoftwareSerial.h>

/*--------------------------------------------------------------------------*/
// RX pin connected to GREEN wire.
// TX pin not used.
/*--------------------------------------------------------------------------*/
SoftwareSerial mySerial(GREEN_WIRE_PIN, 3); // RX, TX

/*--------------------------------------------------------------------------*/
// Globals
/*--------------------------------------------------------------------------*/
volatile unsigned int g_pulseCount = 0;
volatile unsigned long g_pulseTime[200] = { 0 };

/*--------------------------------------------------------------------------*/
// Setup
/*--------------------------------------------------------------------------*/
void setup() {
    // Arduino console port.
    Serial.begin(9600);
    while (!Serial);

    // Control Box serial baud rate.
    mySerial.begin(BAUD_RATE);

    pinMode(BLUE_WIRE_PIN, INPUT);
    attachInterrupt(digitalPinToInterrupt(BLUE_WIRE_PIN), countPulseISR, CHANGE);

    for (int idx=0; idx<10; idx++) Serial.println();
    Serial.println("LethalLillyReader - "__DATE__" "__TIME__);
}

/*--------------------------------------------------------------------------*/
// Main loop
/*--------------------------------------------------------------------------*/
void loop()
{
    static bool isPlaying = false;
    unsigned char ch;

    while (mySerial.available())
    {
        // Read byte from Control Box.
        ch = mySerial.read();

        // Display byte received as hex and binary.
        Serial.print ("[0x");
        Serial.print (ch, HEX);
        Serial.print (" - ");
        Serial.print (ch, BIN);
        Serial.print ("] ");

        // Byte will have the sequence number (1-7) as the right four bits,
        // and the inverse of those bits as the left four bits. At the end
        // of the sequence, a value of 8 is sent the same way.
        //
        //              Hex Binary
        //              --  ---- ----
        // Sequence 1 - E1  1110 0001 (1)
        // Sequence 2 - D2  1101 0010 (2)
        // Sequence 3 - C3  1100 0011 (3)
        // Sequence 4 - B4  1011 0100 (4)
        // Sequence 5 - A5  1010 0101 (5)
        // Sequence 6 - 96  1001 0110 (6)
        // Sequence 7 - E1  1000 0111 (7)
        // End of Seq - 78  0001 1000 (8)

        // Validate. Left nibble AND with right nibble should be zero.
        if ( ((ch & 0xf0 >> 8) & (ch & 0x0f)) != 0)
        {
            // If not, bad byte received.
            Serial.println ("INVALID.");
        }
        else // Good byte received.
        {
            // Check for end of sequence (8).
            if ((ch & 0x0f) == 8)
            {
                Serial.println ("STOPPED.");

                if (isPlaying == true)
                {
                    Serial.print ("Pulses: ");
                    Serial.println (g_pulseCount);

                    isPlaying = false;

                    // Calculate pulse durations.
                    calculatePulseDurations ();
                }
            }
            else
            {
                g_pulseCount = 0;

                Serial.print ("PLAYING #");
                Serial.print (ch & 0x0f);
                Serial.print (" ... ");

                isPlaying = true;
            }
        }
    } // end of while (mySerial.available())
} // end of loop()


/*--------------------------------------------------------------------------*/
// Calculate duration of each pulse.
/*--------------------------------------------------------------------------*/
// NOTE: The pulse times do not shut off immediately. There is about a 34ms
// decay time at the end of each pulse, which makes the Arduino values be
// much shorter than what the Saleae is seeing. I currently do not know how
// this impacts the signal seen by the head, but I will do some testing.
// Adding 37ms to the Arduino calculated "on" time, and subtracting 35ms
// from the "off" time, makes the values much closer.
void calculatePulseDurations ()
{
    for (int idx=0; idx<g_pulseCount-1; idx++)
    {
        unsigned long pulseTime = (g_pulseTime[idx+1] - g_pulseTime[idx]); 
        
        //Serial.print ("(");
        //Serial.print (pulseTime);
        //Serial.print (")");
        
        // Fudge factors to make it match the values that the Saleae
        // logic analyzer calculates.
        if (idx & 1) // Off time
        {
            pulseTime = pulseTime - 35000; // 35.46206 average
        }
        else // On time
        {
            pulseTime = pulseTime + 37000; // 37.0141 average
        }
        //Serial.print ((float)pulseTime/1000); // millis
        Serial.print (pulseTime); // micros
        Serial.print (",");
    }
    Serial.println ();
}


/*--------------------------------------------------------------------------*/
// Pulse counter ISR.
/*--------------------------------------------------------------------------*/
void countPulseISR()
{
    // Store micros time when the pulse changed.
    if (g_pulseCount < (sizeof(g_pulseTime)/sizeof(g_pulseTime[0])))
    {
        g_pulseTime[g_pulseCount] = micros();

        g_pulseCount++;
    }
}

// End of LethalLilyReader.ino

Now that I can capture the serial and pulse data, I should be able to recreate it in the Arduino and write it out to the head, eliminating the Control Box (though without sound, since the Ardunio has no sound hardware).

More to come…

Hacking Home Depot Lethal Lily animatronic – part 2

See Also: intro, part 1, part 2, part 3 and part 4. (Github project.)

WARNING: I am not a hardware person. I’m just a programmer that knows “enough to be dangerous.” I have not measured the voltage/signals coming out of the Control Box to see if they are safe to connect to an Arduino UNO like this. What I am doing could fry the Arduino and/or the Control Box. I don’t know. Consult someone who knows hardware to find out what I am possibly doing wrong…

This article series is a “stream of consciousness” post. I describe the steps as I am working through them. There will be dead-ends, where I learn something new, so try not to scream at your screen when you see me doing something dumb. ;-)

Control Box, meet Arduino UNO…

I used some header/jumper cables (male to female) to go from the four-pin connector’s green wire to an Arudino digital pin (I chose pin 13 since it is next to a ground pin), and the four-pin connector’s black wire to an Arduino ground pin.

Control Box  Arduino UNO
=========== ===========
Blue ------- PIN 13
Black ------ GND
Red not connected
Green not connected
Lethal Lily control box to Arduino.

In the above photo you will see I also have the blue wire also connected, but it is currently not hooked to the Arduino. You only need the green and black Control Box wires going to Arduino I/O and GND pins.

Here is my code, which uses SoftwareSerial to turn PIN 13 (picked only because it was next to the ground pin) in to an RX (receive) serial port pin. The code then loops waiting for a byte to be written by the Control Box. Since I now see that the pattern is four-bits/four-bits-inverted, I even added a bit of code that ANDs each of those four bits together. The result should be zero if it is a valid byte from the Control Box. (This could be done a bit better since now I know the acceptable values are only 1-8, but this is good for a quick test.)

I then print out the byte value in HEX and as binary, and if it’s value is not 8, I print “PLAYING#” and parse out the 3-bit number (1-7). If it is 8, then I print “STOPPED” since that marks the end.

Lather, rinse, repeat… Here is my code:

#include <SoftwareSerial.h>

// RX pin connected to GREEN wire.
// TX pin not used.
SoftwareSerial mySerial(13, 3); // RX, TX


void setup() {
// Arduino console port.
Serial.begin(9600);
while (!Serial);

// Control box sends at 550baud.
mySerial.begin(550);

Serial.println("Running...");
}

void loop()
{
unsigned char ch;

// put your main code here, to run repeatedly:
while (mySerial.available())
{
// Read byte from Control Box.
ch = mySerial.read();

Serial.print ("[");
Serial.print (ch, HEX);
Serial.print (" - ");
Serial.print (ch, BIN);
Serial.print ("] ");

// Validate. Left nibble AND with right nibble should be zero.
if ( ((ch & 0xf0 >> 8) & (ch & 0x0f)) != 0)
{
Serial.println ("INVALID.");
}
else
{
if (ch & 0b1000)
{
Serial.println ("STOPPED.");
}
else
{
Serial.print ("PLAYING #");
Serial.print (ch & 0x0f);
Serial.print (" ... ");
}

}
}
}

// End of file.

Running that, then triggering the sensor, produces the following output:

Running...
[92 - 10010010] PLAYING #2 ... [F8 - 11111000] STOPPED.
[83 - 10000011] PLAYING #3 ... [F8 - 11111000] STOPPED.
[74 - 1110100] PLAYING #4 ... [F8 - 11111000] STOPPED.
[65 - 1100101] PLAYING #5 ... [F8 - 11111000] STOPPED.
[16 - 10110] PLAYING #6 ... [F8 - 11111000] STOPPED.
[7 - 111] PLAYING #7 ... [F8 - 11111000] STOPPED.
[E1 - 11100001] PLAYING #1 ... [F8 - 11111000] STOPPED.
[92 - 10010010] PLAYING #2 ... [F8 - 11111000] STOPPED.

If we can read those bytes, we should be able to send them to the head :)

And now on to the hard part… that blue wire and the pulses that control the servos (or so I suspect at this point).

To be continued…

Hacking Home Depot Lethal Lily animatronic – part 1

See Also: intro, part 1, part 2, part 3 and part 4. (Github project.)

IMPORTANT NOTE: This series follows my typical “document as I discover” format, and will be speculating about something that turned out to be incorrect. Rest assured, I do end up showing how things actually work…

Lethal Lily Swamp Witch is (was?) an animated Halloween prop sold by Home Depot in 2023. Although it looks like you cannot currently buy a full Lethal Lily, replacement parts can be ordered from Seasonal Visions International:

Home | Seasonal Visions International – SVI Service

To see if the animated head could be controlled by an Arduino, I ordered a replacement HEAD, SENSOR, ADAPTER 3AMP and CONTROL BOX.

Lethal Lily replacement parts.

CONTROL BOX

The control box has the following:

  • Three-position slide switch to select modes: Sensor, OFF – Try Me, and Lights Only.
  • Barrel connector input jack for the 5.9V DC 3 amp power supply.
  • Volume knob.
  • Speaker.
  • Four cables that go to the sensor, head, and (I assume) lights.
Lethal Lily control box wiring.

The cables are as follows:

  • Three-pin (green-blue-white) connector that goes to the SENSOR.
  • Four-pin (blue-black-red-green) goes to the HEAD.
  • Two-pin (black-red) that goes to … ? (probably lights)
  • Two-pin (blue-red) that goes to … ? (probably lights)

Inside the box is a small circuit board. I have not removed it to see what is on the bottom, but I suspect the processor is under the black blob.

Lethal Lily control circuit board.

POWER ADAPTER

The power adapter is odd — 5.9V DC, 3 amps.

Lethal Lily 5.9V 3A power adapter.

SENSOR

The SENSOR uses three wires, and I assume it is just a normal motion sensor. A PIR (passive infrared) motion sensor has three wires (voltage, ground, and the output).

Lethal Lily sensor.

HEAD

The head has a four-pin cable coming out of it near the metal shaft that is used to connect it to the frame.

Lethal Lily head cable.

How does it work?

I had hoped to find a bunch of different cables that would run to the head, each operating independent servos. Seeing only a four-wire connector made me wonder if they were sending commands (like I2C messages or something) or doing something else to address all eight (?) servos in the head.

The first thing I did was connect the power adapter and sensor, then hook the head cable up to a Saleae Logic Analyzer. I assumed the black wire to be ground, and the red wire to be power, so I looked at what was going on with the blue and green wires on the ends of the connector.

Lethal Lily head control hooked to a Saleae logic analyzer.

I triggered the sensor seven times (since my understanding is there are seven different patterns Lily can play). What I saw looked promising:

Lethal Lily Saleae capture.

The Green Wire

Above, Channel 0 was hooked to the green wire. This appears to be high except for a few pulses low at the start and end of the sequence, and another set of pulses low at the end.

As I checked the start and stop pulses for each sequence, I saw that they were different. The pulses would vary is width, making it look like it might be I2C or some similar protocol where LOW represents a 0 and HIGH represents a 1 (or vise versa). There are no clock pulses, so if this is encoding a number, it must be doing it based on predefined time, starting to count when it sees the pulse drop. That makes me think it could be normal serial data at some baud rate.

If that is the case, I can work out a sequence of numbers being sent by the green wire at the start and end of each sequence. The signal would drop to indicate the start of data (a “start bit”). Below, “D” is a down pulse, and “U” is back up. I can easily “see” eight in each of the start/end sequences:

  1. UDDDDUUU / DDDUUUUD
  2. DUDDUDUU / DDDUUUUD
  3. UUDDDDUU / DDDUUUUD
  4. DDUDUUDU / DDDUUUUD
  5. UDUDDUDU / DDDUUUUD
  6. DUUDUDDU / DDDUUUUD
  7. UUUDDDDU / DDDUUUUD

Looking at the pulses this way shows a pattern. The end pulses are the same for each sequence. Using a serial decoder and and playing around with the baud rate until the bits decoded match where the pulses are, gives me something like this:

That is using a value of 550 baud. I have no idea if this is correct, but if so, it gives the following values for the start of each sequence. Note that the bits are being written least bit first (so, right to left) so UDDDDUUU becomes 11100001 in binary.

E1 - 11100001
D2 - 11010010
C3 - 11000011
B4 - 10110100
A5 - 10100101
96 - 10010110
87 - 10000111

And at the end of each pattern, there is a 0x78 sent (as shown in the screen shot above):

78 - 11100001

I see a pattern! Each set of eight pulses is a 4-bit value, then the value with the bits inverted!

E1 - 11100001 - 1110 and 0001 (1)
D2 - 11010010 - 1101 and 0010 (2)
C3 - 11000011 - 1100 and 0011 (3)
B4 - 10110100 - 1011 and 0100 (4)
A5 - 10100101 - 1010 and 0101 (5)
96 - 10010110 - 1001 and 0110 (6)
87 - 10000111 - 1000 and 0111 (7)

The values go from 1 to 7 — matching the number of patterns the control box can perform. It looks like 0 is not used.

As for the end sequence of 78, that is the same pattern:

78 - 01111000 - 0111 and 1000 (8)

I do not know what these values instruct the head to do, but at least now I should be able to recreate sending those pulses out via an Arduino or something just by writing out a byte at 550 baud.

The Blue Wire

The data on the blue wire looks like pulses of different widths. My understanding is a servo position is set based on how wide of a pulse is sent. The head must be receiving these pulses and passing them to the different servos somehow. Speculation: Perhaps it sends eight pulses that go to servo 1, 2, 3, etc. and then restarts back at servo 1. (Me from the future: It does not…)

To figure out how many pulses there are for each of the seven sequences, I used the Saleae Logic Analyzer and highlighted a range of pulses. It showed me that each sequence has this many “rising edges” (where the pulse goes up):

  1. 85 pulses
  2. 87
  3. 75
  4. 87
  5. 100
  6. 96
  7. 79
  8. (back to pattern 1) 84 … ?

Speculation: Seeing that the first pattern 1 reported 85 pulses in the Saleae Logic Analyzer, then the second one reported 84, tells me I may not have things set up properly with my Saleae.

This what not what I hoped to see. I expected. If there are eight servos, each number should be a multiple of eight. This clearly does not be the case. Perhaps the pulses can stop at any time (say, updating 8 servos, 8 servos, 8 servos, then just 3 for a total of 27 pulses)? I would still expect every pattern to end with pulses that set the head back to a default “off” state. Perhaps the head does that on its own when it receives the 0x78 “end” byte? Since the start bytes are different for each pattern, I suspect the head must need to know that for some purpose, as well. (Me from the future: This is foreshadowing…)

Also, I only think there are eight servos because of a reference in this Home Depot press release. There is an unofficial wiki that says there are nine servos.

I also do not assume all servos are independently controlled. If each eye has it’s own left/right servo, one pulse could be sent to each eye so the move together. At some point I may have to take this thing apart and look inside.

Until then, let’s see what eight servos might control:

  1. Head tilt forward and backwards.
  2. Head tilt left and right.
  3. Head turn left and right.
  4. Left eye looks left and right.
  5. Left eye looks up and down.
  6. Right eye looks like and right.
  7. Right eye looks up and down.
  8. Eyelids blink.
  9. Mouth open and close…if this is a servo?

Maybe this is why the wiki says nine?

And, if it is nine, but they tied the eyes together, it might really look like this to the control box signals:

  1. Head tilt forward and backwards.
  2. Head tilt left and right.
  3. Head turn left and right.
  4. Both eyes looks left and right.
  5. Both eyes looks up and down.
  6. Both eyelids blink.
  7. Mouth open and close…if this is a servo?

That could mean that only seven pulses are needed in the sequence.

Before I can proceed, I need to hook up the head and see what all motions it does.

To be continued…

Hacking Home Depot Lethal Lily animatronic props?

See Also: intro, part 1, part 2, part 3 and part 4. (Github project.)

As a lifelong fan of Halloween, I certainly think we are living in a golden age when it comes to store-bought props and decorations. There are so many animated and light up props available each year at temporary stores (like Spirit) or, for some reason, home improvement stores like Home Depot and Lowes.

In 2023, Lethal Lily was introduced. This prop has eights servos to control its head – eyes move left, right, up and down. Eyelids blink. Mouth opens. And the head can turn and tilt forward and backwards (so it seems from the video).

Here was their press release, which contains a link to a video of Lily:

https://corporate.homedepot.com/news/products/innovation-quality-and-value-introducing-home-depots-2023-halloween-product-lineup

I am making this post in case someone else is considering hacking on these to control them via an Arduino, Raspberry Pi or some other keyword I would list here if I could think of it.

More to come… Leave a comment if you found this page, looking for this information.

Halloween DMX lighting and sound – introduction

Years ago, I worked with a local guy on bringing in computer controlled lighting and sound to a local haunted house attraction. The original attraction just used light bulbs of different colors and actors to make everything happen. Our upgrade included the following:

  • We used a cheap Windows PC and VenueMagic show control software.
  • We ran a chain of RGB DMX lights throughout the attraction.
  • About 14 speakers were installed, all wired back to a stack of cheap Pyle brand amplifiers.
  • A cheap USB hub and some $2 USB sound cards was used to get multi-channel audio from VenueMagic, and it worked great.
  • We installed pressure mats to trigger audio and effects.
  • I built an Arduino input box to read triggers from pressure mats and send them to VenueMagic as serial data.
  • There was also a DMX controlled relay board that was used to trigger air jets and control pneumatic doors.
  • When building regulations changed a few years into operation, the haunt was wired with illuminated exit signs and smoke detectors. Those were brought into the system so it could halt the show and turn on white work lights for safety.

The end result was a living, breathing attraction with flickering “flame” light, atmospheric moonlighting, strobe effects, as well as immersive surround sound throughout.

The VenueMagic software was, by far, the most expensive element of this project, and also the one that provided the most headaches. While the software itself was amazing and powerful, running on Windows was not. We had numerous issues over the years with Windows crashing, USB devices failing to be recognized, audio devices being remapped (requiring editing the show control configuration), etc.

Once the computer was in control, if it went down, you lost the entire show. The lights had a backup mode (we called it “party mode”) where they would begin randomly cycling through colors if there was no DMX signal. At least it didn’t leave the guests in the dark, but a silent haunt with circus lighting was far from scary.

In upcoming articles, I plan to discuss various ways you can show with complex lighting and audio, and make it play without needing a PC.

Just in time for the holidays ;-)

Happy Halloween in November!

A few side projects keep me busy during the year. One is doing things for local festivals (show guides, websites, newspaper ads, TV commercials, etc.) and the other is maintaining my haunted house website: www.dmhauntedhouses.com

During October, I visit with all the local haunted attractions to get information from that website. I do video interviews, create custom audio/video effects for them, and other projects. Over the years I have done quite a bit in this area, from building BASIC-Stamp based prop controllers to doing complex DMX lighting/audio show control programs.

For 2018, I am going to start documenting my projects, and making plans available for those who want to recreate them. I also plan on making items available pre-built for those who just want to use and not build.

More to come…

2014 Halloween project update

I have been sent a few things I will be using to make a prototype for the Halloween project. The main features will be playing audio on demand, and switching lights (or other 120V items) on and off in time to the audio. Here are the items I will be evaluating:

  • MP3 Shield ($19.99) from CutiDigi.com. This shield has its own flash storage and lets you load MP3 files over from SD memory cards or USB thumb drives. In addition to control buttons on the shield itself (vol+/-, pref, next, play), it also has buttons to start up the copy operation and put the unit to sleep. There is a 1/8″ headphone style jack for getting audio out. It is controlled via serial (tx/rx) and can be made to play a specific track number off the memory.
  • Relay Shield ($7.59) from e-Bay seller happyvalley009. This shield has four relays that can handle 120V up to 3amps, which is a small amount but enough for our needs. It is dangerous to run 120V in to a shield like this, as a short could really cause some problems. A better solution might be to use a separate relay board ($8.49 with Amazon Prime shipping) that is controlled without being attached to the Arduino itself. That would let it be physically separate and still be controlled the same way (and, this one can handle more amperage).

As soon as my funding source returns from vacation, we will order more items and begin working on a prototype.

2014 Arduino projects for Halloween

I have been tasked with creating two control systems for some Halloween attractions this year. I have a small budget for building the prototypes, and if they work, then I will be building a dozen or so of the units. I thought it might be fun to document the entire process here.

There are two projects:

1. A device will sense motion, then begin playing sound and toggle four outlets in a sequence that goes along with the audio.

2. A device will sense its location, and play a specific sound based on that location. It will have a fallback mode where buttons will trigger the sounds, for manual operation.

I plan to use low-cost Arduinos since there are many add-on Shields available for it to handle things like this.

Audio could be played in high quality using a cheap ($20) MP3 add-on, or, with a small amount of hardware (and a cheap SD card reader), lower quality audio can be played directly by the Arduino.

For triggering, the I/O pins will be used, hooked to a motion sensor. For the proximity sensor, I am researching iBeacon style tech (BLE, bluetooth low energy) or IR (infrared). Right now, it seems we could use cheap IR remotes, with a button taped down beaming and endless pulse. The Arduino can hook up a $1 IR receiver and software could decode the pulses to see which zone it is in.

For the outlets, there are $8 high voltage relay boards that can be wired to the Arduino’s Digital Out pins, and even a cheap $7.50 4-channel relay shield that can handle 120 volts 3 amps on each relay. The Shield is a nice idea, but dumping 120V in to the Arduino could be a problem if there was any kind of short.

I will document the various products I have found so far, soon.

To be continue…