Need help coding a Ghostbusters doodad

Hey, everyone.
I'd like to make a doodad similar to something I saw Adam Savage make for his Ghostbusters costume. It'd be a smallish box with a bar graph controlled with a pot, and a few LEDs that blink in sequence, and then blink more rapidly as the pot is turned up. I'll attach a drawing of what it'll look like.
I'm very new to this and I apologize if I get the words/terminology wrong.
I can make a bar graph/pot combo function and I can do some basic LED control from various tutorials. But I don't know how to tell a board to do more than one "thing". I have an OSEPP UNO R3 for the project.


What I'd like the three LEDs to do is one turns on, the second one comes on, then the third one blinks twice and then then all turn off and restart the pattern. It'd be cool if the pattern speeds up the higher the bar graph is, but that part isn't necessary.
Thanks in advance for your help!

I also wanted to say that, while yes, I'm lazy and would love for somebody else to just make the sketch for me, I'm willing to learn if there are some applicable tutorials or something I should be pointed to.

Begin with getting all the components and the power supply for the device. See if you can arrange them to let you determine an appropriate enclosure for the doodad. Then begin to learn how to control each component with your code.

I point to this one:

Hello corbidorbidoodle

Check and try this led sequencer sketch to get started in your project.

#define usl unsigned long // I´m lazy to type 

constexpr int LedPins[] {9, 10, 11};

struct LEDSEQUENCER
{
  usl span;
  int pattern[sizeof(LedPins) / sizeof(LedPins[0])];
  usl now;
  int control;
};
LEDSEQUENCER ledSequencers[]
{
  { 500, 0, 0, 0, 0, HIGH},
  { 1000, 1, 0, 0, 0, LOW},
  { 500, 0, 0, 0, 0, LOW},
  { 1000, 0, 1, 0, 0, LOW},
  { 500, 0, 0, 0, 0, LOW},
  { 1000, 0, 0, 1, 0, LOW},
  { 500, 0, 0, 0, 0, LOW},
  { 1000, 0, 0, 1, 0, LOW},
  // add pattern if needed
};

void setup()
{
  for (auto LedPin : LedPins)
  {
    pinMode ( LedPin, OUTPUT);
    digitalWrite(LedPin, HIGH);
    delay(1000);
    digitalWrite(LedPin, LOW);
    delay(1000);
  }
}
void loop()
{
  usl currentMillis = millis();
  for (auto &ledSequencer : ledSequencers)
  {
    if (currentMillis - ledSequencer.now >= ledSequencer.span && ledSequencer.control)
    {
      static int number = 0;
      int element = 0;
      ledSequencer.control = LOW;
      number = (number + 1) % (sizeof(ledSequencers) / sizeof(ledSequencers[0]));
      ledSequencers[number].control = HIGH;
      ledSequencers[number].now = currentMillis;
      for (auto LedPin : LedPins) digitalWrite(LedPin, ledSequencers[number].pattern[element++]);
    }
  }
}

Have a nice day and enjoy coding in C++.

1 Like

Why do you need the static int number?

Is there a way to compute the next step in the sequence (current iteration in an iterable?) without maintaining a separate static tracker? It isn't a problem with this particular dataset, but if you happened to have two sequencers initialized as control=HIGH, it seems like 'number' would step twice as fast and get misaligned.

Hello DaveX

All your assumptions are correct.

The static variable is used to manage access to the pattern.

A workaround would be to have all patterns have their own number.

A secound sequence-timer ....... I´ve to think about :nerd_face:

I was wondering if something like:

    int number = 0;
    for (auto &ledSequencer : ledSequencers){
      ...
      ++number;
    }

could be done with something like:

    for (int i = 1, j = 1; i < 5; ++i, j*=-1) 
        Serial.println(i*j);

or if there's a trick like:

number = (ledSequencer - ledSequencers)/sizeof(ledSequencers[0]);

Here's the trick:

int number = &ledSequencer-ledSequencers; // Finds the index of the current element

And in the code:

#define usl unsigned long // I´m lazy to type 

constexpr int LedPins[] {9, 10, 11};

struct LEDSEQUENCER
{
  usl span;
  int pattern[sizeof(LedPins) / sizeof(LedPins[0])];
  usl now;
  int control;
};
LEDSEQUENCER ledSequencers[]
{
  { 500, 0, 0, 0, 0, HIGH},
  { 1000, 1, 0, 0, 0, LOW},
  { 500, 0, 0, 0, 0, LOW},
  { 1000, 0, 1, 0, 0, LOW},
  { 500, 0, 0, 0, 0, LOW},
  { 1000, 0, 0, 1, 0, LOW},
  { 500, 0, 0, 0, 0, LOW},
  { 1000, 0, 0, 1, 0, LOW},
  // add pattern if needed
};

void setup()
{
  for (auto LedPin : LedPins)
  {
    pinMode ( LedPin, OUTPUT);
    digitalWrite(LedPin, HIGH);
    delay(1000);
    digitalWrite(LedPin, LOW);
    delay(1000);
  }
}
void loop()
{
  usl currentMillis = millis();
  for (auto &ledSequencer : ledSequencers)
  {
    if (currentMillis - ledSequencer.now >= ledSequencer.span && ledSequencer.control)
    {
      int number = &ledSequencer-ledSequencers;
      int element = 0;
      ledSequencer.control = LOW;
      number = (number + 1) % (sizeof(ledSequencers) / sizeof(ledSequencers[0]));
      ledSequencers[number].control = HIGH;
      ledSequencers[number].now = currentMillis;
      for (auto LedPin : LedPins) digitalWrite(LedPin, ledSequencers[number].pattern[element++]);
    }
  }
}

On the other hand, if there's only one sequence active at a time, and it's index in recorded in number you could move static int number out to the top level of loop and instead of looking at the whole list sequencers with for(...), you could look at only the active one:

#define usl unsigned long // I´m lazy to type 

constexpr int LedPins[] {9, 10, 11};
constexpr int nLedPins = sizeof(LedPins) / sizeof(LedPins[0]);

struct LEDSEQUENCER
{
  usl span;                 // ms of time in state
  int pattern[nLedPins];    // Settings of the LEDs in this state
  usl now;                  // Timestamp of last change
  int control;              // whether active or not
};
LEDSEQUENCER ledSequencers[]
{
  { 500, 0, 0, 0, 0, LOW},
  { 1000, 1, 0, 0, 0, LOW},
  { 500, 0, 0, 0, 0, LOW},
  { 1000, 0, 1, 0, 0, LOW},
  { 500, 0, 0, 0, 0, HIGH},   // start with this one
  { 1000, 0, 0, 1, 0, LOW},
  { 500, 0, 0, 0, 0, LOW},
  { 1000, 0, 0, 1, 0, LOW},
  // add pattern if needed
};
constexpr int nLedSequencers = sizeof(ledSequencers) / sizeof(ledSequencers[0]);
static int number = 0;

void setup()
{
  for (auto LedPin : LedPins) // setup and excercise the ledPins
  {
    pinMode ( LedPin, OUTPUT);
    digitalWrite(LedPin, HIGH);
    delay(1000);
    digitalWrite(LedPin, LOW);
    delay(1000);
  }
  Serial.begin(115200);
  for (auto &ledSequencer : ledSequencers) { // find the index of the last HIGH
    if (ledSequencer.control == HIGH) {
      number =  &ledSequencer - ledSequencers;
    }
  }
}
void loop()
{
  usl currentMillis = millis();  // remember the current time
  auto &ledSequencer = ledSequencers[number];  // Uses a reference & because we need to modify data
  if (currentMillis - ledSequencer.now >= ledSequencer.span && ledSequencer.control)
  {
    int element = 0;
    ledSequencer.control = LOW;
    number = (number + 1) % nLedSequencers;
    ledSequencers[number].control = HIGH;
    ledSequencers[number].now = currentMillis;
    for (auto LedPin : LedPins) digitalWrite(LedPin, ledSequencers[number].pattern[element++]);
  }
}

Thanks for all the great replies, everybody! I've only got a couple hours a week to work on this stuff, so sorry for the late reply.
I'll try some of this out today!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.