Mercenary functions only work alone, need them to work together to pull off the heist

Hello helpful Arduino friends!

I'm stuck in a situation where each function works well on it's own, but when I try to run them together they behave differently or do nothing at all. Before I take this code and spread it over three different duo boards, do any of you fine folks have ideas for how I could tidy things up? I suspect there's something about the sensors and the FastLED library that aren't giving each other room to do more than one iteration of what is essentially the same task.
An earlier version of this code worked in the bench testing phase, but I recall struggling with this then a bit then too.

This is my first time posting my code, pls be kind, smh.

/*
    An interactive addition to a large installation.
        The bells and whistles of a climber.
---------------------------------------------------------------------------------------
//INTERACTIVE AREA 1: Lower Light Tower: Oil Pump
    INPUT:
      IR Sensor: reads physical Input from Crank (Map Number of rotations to Number of LEDs)
    OUTPUT:
      LED: Lower Tower Fills with Red Light from the bottom up.
      AUDIO: Local speaker, low Volume, Oil Derrick Metal cranking
    -Reset: After there is no Physical Input for 5 seconds, light begins to ‘leak’
//INTERACTIVE AREA 2: Mid Light Tower: Oil Drill
    INPUT:
      IR Sensor reads physical Input from Wheel (Map Rotations to LEDs)
    OUTPUT:
      LED: Middle Tower Fills with Yellow Light from the bottom up.
      AUDIO: Local Speaker, low Volume Drilling into Earth
//INTERACTIVE AREA 3: Upper Light Tower: Rope Pull
    INPUT:
      IR Sensor reads physical Input from pulley (Map Rotations to LEDs)
    OUTPUT:
      LED: Upper Tower Fills with Green Light from the bottom up.
      AUDIO: Local Speaker, low Volume Wench/Squeaking sound
//IN THE EVENT ALL THREE TOWERS ARE AT MAX CAPACITY
      INPUT: a function that looks for all three's top vals to be true
      OUTPUT: ALL TOWERS to WHITE and pulse 3 times while playing victory sound, then ‘leaks’ over 5-10 seconds over the entire tower
      Audio: Big Old Bell Reward Sound (Not to match exhibit bell)
      Light: Fade to White over 1-2 seconds. Gradual Intensity Blink 5-10 seconds
-------------------------------------------------------------------------------------*/                                                      
//LIBRARIES
  #include "Arduino.h"
  #define FASTLED_FORCE_SOFTWARE_SPI
  #include <FastLED.h>
//LEDS
    const int Brightness = 100;

    const int PumpDAT = 8;
    const int PumpCLK = 12;
    const int PumpNUM = 130;
    CRGB PumpLED[PumpNUM];
    
    const int DrillDAT = 9;
    const int DrillCLK = 11;
    const int DrillNUM = 82;
    CRGB DrillLED[DrillNUM];

    const int RopeDAT = 10;
    const int RopeCLK = 13;
    const int RopeNUM = 78;
    CRGB RopeLED[RopeNUM];

//AUDIO      
  const int PumpAudio = 5; //circuit to WavTrigger 1, edit to Left channel
  const int DrillAudio = 4; //circuit to WavTrigger 1, edit to Right channel
  const int RopeAudio = 2; //circuit to WavTrigger 2
  const int BellAudio = 3;  //circuit to WavTrigger 1 & 2

//SENSORS
  unsigned long CurrentTime;
  const int WaitingPeriod = 750;
  //PUMP
    const int PumpSensor = 14;
    const int PumpRatio = 27; //used for mapping
    //PUMP VARIABLES
      bool Pumping; //used for audio
      bool Pumped; //used for celebration
      unsigned long LastPump; //used to generate time out
      unsigned long PumpTimeOut; //used for timing
      int Pump; //holds latest sensor reading
      int LastPumpRead; //holds previous sensor reading
      int PumpCount = 0; //mapped version of PC
      int PC = 0; //counts pumps
  //DRILL
    const int DrillSensor = 15;
    const int DrillRatio = 25;
    //DRILL VARIABLES
      bool Drilling;
      bool Drilled;
      unsigned long LastDrill;
      unsigned long DrillTimeOut;
      int Drill;
      int LastDrillRead;
      int DrillCount = 0;
      int DC = 0;
  //ROPE
    const int RopeSensor = 16;
    const int PullRatio = 25;
    //ROPE VARIABLES
      bool Pulling;
      bool Pulled;
      unsigned long LastPull;
      unsigned long PullTimeOut;
      int Pull;
      int LastPullRead;
      int PullCount = 0;
      int RC = 0;
//---------------------------------------------------------------------------------------

void setup() {
//SERIAL
  
  Serial.begin(115200);
  Serial.println("");
  Serial.println("OZZY OZZY OZZY!!");
  Serial.println("OIL, OIL, OIL!!");
  
//LEDS
  FastLED.addLeds<DOTSTAR, PumpDAT, PumpCLK, BGR>(PumpLED, PumpNUM);
  FastLED.addLeds<DOTSTAR, DrillDAT, DrillCLK, BGR>(DrillLED, DrillNUM);
  FastLED.addLeds<DOTSTAR, RopeDAT, RopeCLK, BGR>(RopeLED, RopeNUM);
  FastLED.setBrightness(Brightness);
//AUDIO
  pinMode(PumpAudio,OUTPUT);
  pinMode(DrillAudio,OUTPUT);
  pinMode(RopeAudio,OUTPUT);
  pinMode(BellAudio,OUTPUT);
//SENSORS
  pinMode(PumpSensor, INPUT_PULLUP);
  pinMode(DrillSensor, INPUT_PULLUP);
  pinMode(RopeSensor, INPUT_PULLUP);
  Pumped = false;
  Pumping = false;
  Drilled = false;
  Drilling = false;
  Pulled = false;
  Pulling = false;


//STARTUP ANIMATION
  FastLED.clear();
  FastLED.show();
  for (int i = 0; i <PumpNUM; i++) {
    PumpLED[i] = CRGB::Red;
    FastLED.show();
    digitalWrite(PumpAudio, HIGH);
    delay(10);
  }
  digitalWrite(PumpAudio, LOW);

  for (int i = 0; i <DrillNUM; i++) {
    DrillLED[i] = CRGB::Yellow;
    FastLED.show();
    digitalWrite(DrillAudio, HIGH);
    delay(10);
  }
  digitalWrite(DrillAudio, LOW);

  for (int i = 0; i <RopeNUM; i++) {
    RopeLED[i] = CRGB::Red;
    FastLED.show();
    digitalWrite(RopeAudio, HIGH);
    delay(10);
  }
  digitalWrite(RopeAudio, LOW);
  digitalWrite(BellAudio, HIGH);
  delay(1000);
  digitalWrite(BellAudio, LOW);
  delay(1000);
  FastLED.clear();
  FastLED.show();
  delay(1000);
}//end void setup
//---------------------------------------------------------------------------------------
void loop() {
  CurrentTime = millis();
  /*
    if (Pumped == true &&
        Drilled == true &&
        Pulled == true ){
      Celebrate(); 
    }
  */

  PumpItUp();
  //DrillItDown();
  //PullItOut();

  //LEDTEST();
  //SENSORTEST();
  //AUDIOTEST();

}//end void loop

void PumpItUp() {
  //READ SENSOR
    int Pump = digitalRead(PumpSensor);
    if (Pump != LastPumpRead) { 
      if (PC < PumpRatio) {
          PC++;
          Pumping = true;
          LastPump = CurrentTime;
          PumpTimeOut = (LastPump + WaitingPeriod);
      }
    }
    LastPumpRead = Pump;
  //AUDIO
    if (Pumping == true) {
      digitalWrite(PumpAudio, HIGH);
    }
    if (Pumping == false) {
      digitalWrite(PumpAudio, LOW);
    }
  //BOUNDARIES
    if (PC >= PumpRatio) {
      Pumped = true;
      }
    if (PC < 0) { PC = 0; }
    if (PC >= PumpRatio+1) { PC--; PC--; PC--; }
  //LEDS & FILTERS
    PumpCount = map(PC, 1, PumpRatio, 0, PumpNUM);
    int k = PumpCount;
    for(int i = 0; i < k; i++){ 
      PumpLED[i] = CRGB::Red;
      }
    FastLED.show(); 
    delay(10);
  //TIMEOUT
    if (CurrentTime >= PumpTimeOut) {
      PC--;
      Pumping = false;
      Pumped = false;
      for(int i = PumpNUM; i > k; i--){
        PumpLED[i].setRGB(0,0,0);
      }
      FastLED.show(); 
      delay(10);
    }
}//end void PumpItUp

void DrillItDown() {
  //READ SENSOR
    int Drill = digitalRead(DrillSensor);
    if (Drill != LastDrillRead) { 
      if (DC < DrillRatio) {
        DC++;
        Drilling = true;
        LastDrill = CurrentTime;
        DrillTimeOut = (LastDrill + WaitingPeriod);
      }
    }
    LastDrillRead = Drill;
  //AUDIO
    if (Drilling == true) {
      digitalWrite(DrillAudio, HIGH);
    }
    if (Drilling == false) {
      digitalWrite(DrillAudio, LOW);
    }
  //BOUNDARIES
    if (DC >= DrillRatio) {
      Drilled = true;
      }
    if (DC < 0) { DC = 0; }
    if (DC >= DrillRatio+1) { DC--; DC--; DC--; }
  //FILTERS AND LEDS
    DrillCount = map(DC, 1, DrillRatio, 0, DrillNUM);
    int k = DrillCount;
    for(int i = 0; i < k; i++){ 
      DrillLED[i] = CRGB::Yellow;
      }
    FastLED.show(); 
    delay(5);
  //TIMEOUT
    if (CurrentTime >= DrillTimeOut) {
      DC--;
      Drilling = false;
      Drilled = false;
      for(int i = DrillNUM; i > k; i--){
        DrillLED[i].setRGB(0,0,0);
      }
      FastLED.show(); 
      delay(5);
    }
}//end void DrillItDown

void PullItOut() {
  //READ SENSOR
    int Pull = digitalRead(RopeSensor);
    if (Pull != LastPullRead) {
      if (RC < PullRatio) {
        RC++;
        Pulling = true;
        LastPull = CurrentTime;
        PullTimeOut = (LastPull + WaitingPeriod);
      }
    }
    LastPullRead = Pull;

  //AUDIO  
    if (Pulling == true) {
      digitalWrite(RopeAudio, HIGH);
    }
    if (Pulling == false) {
      digitalWrite(RopeAudio, LOW);
    }
  //BOUNDARIES
    if (RC >= PullRatio) {
      Pulled = true;
      }
    if (RC < 0) { RC = 0; }
    if (RC >= PullRatio+1) { RC--; RC--; RC--; }
  //FILTERS AND LEDS
    PullCount = map(RC, 1, PullRatio, 0, RopeNUM);
    int k = PullCount;
    for(int i = 0; i < k; i++){ 
      RopeLED[i] = CRGB::Green;
    }
    FastLED.show(); 
    delay(5);
  //TIMEOUT
    if (CurrentTime >= PullTimeOut) {
      RC--;
      Pulling = false;
      Pulled = false;
      for(int i = RopeNUM; i > k; i--){
        RopeLED[i].setRGB(0,0,0);
      }
      FastLED.show(); 
      delay(5);
    }
}//end void PullItOut

void Celebrate() {
  //AUDIO - turn off machines, play buzzer
    digitalWrite (PumpAudio, LOW);
    digitalWrite (DrillAudio, LOW);
    digitalWrite (RopeAudio, LOW);
    digitalWrite (BellAudio, HIGH);
  //LED FLASH ON
    for(int i = 0; i < PumpNUM; i++){ 
		  PumpLED[i].setRGB(255,255,255);
    }
    for(int i = 0; i < DrillNUM; i++){
      DrillLED[i].setRGB(255,255,255);
    }
    for (int i = 0; i < RopeNUM; i++){
      RopeLED[i].setRGB(255,255,255);
	  }
    FastLED.show(); 
    delay(500);
  //LED FLASH OFF
    for(int i = 0; i < PumpNUM; i++){ 
		  PumpLED[i].setRGB(0,0,0);
    }
    for(int i = 0; i < DrillNUM; i++){
      DrillLED[i].setRGB(0,0,0);
    }
    for (int i = 0; i < RopeNUM; i++){
      RopeLED[i].setRGB(0,0,0);
	  }
    FastLED.show(); 
    delay(500);
  //LED FLASH ON
    for(int i = 0; i < PumpNUM; i++){ 
		  PumpLED[i].setRGB(255,255,255);
    }
    for(int i = 0; i < DrillNUM; i++){
      DrillLED[i].setRGB(255,255,255);
    }
    for (int i = 0; i < RopeNUM; i++){
      RopeLED[i].setRGB(255,255,255);
	  }
    FastLED.show(); 
    delay(500);
  //LED FLASH OFF
    for(int i = 0; i < PumpNUM; i++){ 
		  PumpLED[i].setRGB(0,0,0);
    }
    for(int i = 0; i < DrillNUM; i++){
      DrillLED[i].setRGB(0,0,0);
    }
    for (int i = 0; i < RopeNUM; i++){
      RopeLED[i].setRGB(0,0,0);
	  }
    FastLED.show(); 
    delay(500);
  //LED FLASH ON
    for(int i = 0; i < PumpNUM; i++){ 
		  PumpLED[i].setRGB(255,255,255);
    }
    for(int i = 0; i < DrillNUM; i++){
      DrillLED[i].setRGB(255,255,255);
    }
    for (int i = 0; i < RopeNUM; i++){
      RopeLED[i].setRGB(255,255,255);
	  }
    FastLED.show(); 
    delay(2000);
  //LED FLASH OFF
    for(int i = 0; i < PumpNUM; i++){ 
		  PumpLED[i].setRGB(0,0,0);
    }
    for(int i = 0; i < DrillNUM; i++){
      DrillLED[i].setRGB(0,0,0);
    }
    for (int i = 0; i < RopeNUM; i++){
      RopeLED[i].setRGB(0,0,0);
	  }
    FastLED.show(); 
    delay(10);
  //RESET  
    digitalWrite (BellAudio, LOW);
    Pumped = false;
    Pumping = false;
    PC = 0;
    Drilled = false;
    Drilling = false;
    DC = 0;
    Pulled = false;
    Pulling = false;
    RC = 0;
}//end void Celebrate

void fadeall() { for(int i = 0; i < 130; i++) {
  PumpLED[i].nscale8(200);
  DrillLED[i].nscale8(200);
  RopeLED[i].nscale8(200);
  }
}//end void fadeall
void LEDTEST() {
  	for(int i = 0; i < PumpNUM; i++) {
      PumpLED[i] = CRGB::Red;
      RopeLED[i] =  CRGB::Green;
      DrillLED[i] = CRGB::Yellow;
      FastLED.show();
      fadeall();
      delay(10);
    } 
	  for(int i = (100)-1; i >= 0; i--) {
      PumpLED[i] = CRGB::Red;
      DrillLED[i] = CRGB::Yellow;
      RopeLED[i] =  CRGB::Green;
      FastLED.show();
      fadeall();
      delay(10);
  	}
}//end void LEDTEST

void SENSORTEST() {
    
      int Pump = digitalRead(PumpSensor);
      Serial.print("Pump: ");
      Serial.println(Pump);
    /*
    int Drill = digitalRead(DrillSensor);
    Serial.print("Drill: ");
    Serial.println(Drill);
    */
    //int Pull = digitalRead(RopeSensor);
    //Serial.print("Rope: ");
    //Serial.println(Pull);
    
}//end void SENSORTEST

void AUDIOTEST() {
        digitalWrite(PumpAudio, HIGH);
        Serial.println("Pump High");
  delay(1000);
        digitalWrite(PumpAudio, LOW);
        Serial.println("Pump Low");
  delay(1000);
        digitalWrite(DrillAudio, HIGH);
        Serial.println("Pump High");
  delay(1000);
        digitalWrite(DrillAudio, LOW);
        Serial.println("Pump Low");
  delay(1000);
        digitalWrite(RopeAudio, HIGH);
        Serial.println("Bell High");
  delay(1000);
        digitalWrite(RopeAudio, LOW);
        Serial.println("Bell Low");
  delay(1000);
        digitalWrite(BellAudio, HIGH);
        Serial.println("Bell High");
  delay(1000);
        digitalWrite(BellAudio, LOW);
        Serial.println("Bell Low");
  delay(1000);
}```

Hello 13andit

I assume that you have written the programme by yourself, then it is quite easy to find the error.

There's a trick to figuring out why something isn't working:

Use a logic analyzer to see what happens.
Put Serial.print statements at various places in the code to see the values of variables, especially ones that control the motors, and determine whether they meet your expectations.

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

I have written the code myself yes.
There are no motors??
I took a lot of the serial stuff out bc I noticed it slowed things down pretty significantly, esp when things were trying to run at the same time.

Did you test each little addition as you wrote the program?

Every function works on its own. I'm tryna figure out ways they might be stopping each other from working when they're running at the same time, like what resource is being overtaxed or something.

This by far a too short description of what your code shall do.
I have read your code. Your code is structured which is good.
Still
Without a functional description written in normal words I have no idea what your code is doing in detail and how it works together.

You have an unusual way of coding non-blocking timing
example

          PumpTimeOut = (LastPump + WaitingPeriod);

and somewhere else in the code

    if (CurrentTime >= PumpTimeOut) {

The usual way to code non-blocking timing is
in a single line of code use

  • currentTime
  • a "startTime"
  • a time-interval

example:

if (currentTime - LastPump >= WaitingPeriod) {

coded in this way the I claim there is more coherence than dividing this into two places where other conditions are used

    if (Pump != LastPumpRead) { 
      if (PC < PumpRatio) {
          PC++;
          Pumping = true;
          LastPump = CurrentTime;
          PumpTimeOut = (LastPump + WaitingPeriod);
      }

without a normal worded functional description it would take me 20 hours or more to analyse what your code is doing and I would be still insecure of what it really is.

So as the next step
post a normal worded functional description of what your code is doing.
This will speed up understanding your code by a factor 10

best regards Stefan

It compiles!

OZZY OZZY OZZY!!

OIL, OIL, OIL!!

Now for the drawing/schematic...

Is the explanation at the top of the code not normal enough to explain what's going on here?
3x there's a sensor that reads input from a mechanism and plays a sound while lighting up lights.

Sometimes, a picture (or a drawing) is worth a thousand words.
ozzy

I want it to run all three, but it doesn't want to. :confused:
When the sensors don't see any change, nothing should happen, when someone pumps, pulls, or drills the mechanisms, the corresponding audio plays and the corresponding LEDS begin to light up, working their way up to the top.
I was hoping one board could listen to three sensors and run the whole thing, but I'm wondering if it'd be easier to just pull this apart onto three duos and use the code I know works.

I know you want a circuit diagram but I did it all in fritzing and it's a nightmare to read.
annnd i just realized i've been saying duo when i mean UNO
but fwiw:
it's an UNO with

  • 4 audio outputs running to 2x wav triggers thru 2x class d amps to 3x speakers
  • 3 sensor inputs running to 3 t slot photoresistors installed into mechanisms that can be turned
  • 3x clock/data lines running to a LOT of dotstars powered by an external source
    I have no concerns about the circuit's function as it has passed all my tests.

it's a test i only used to make sure all the audio was working

When all is said and done, I was hoping this is what the main loop would look like. While installing everything I used the tests to make sure the individual components worked. Sorry for the confusion in leaving this section with varying degrees of commenting.
I want to know what these three functions don't play nice together.

void loop() {
  CurrentTime = millis();
    if (Pumped == true &&
        Drilled == true &&
        Pulled == true ){
      Celebrate(); 
    }
  PumpItUp();
  DrillItDown();
  PullItOut();
}

answering this question becomes possible if you describe two things:

  1. what behaviour do you observe when these three functions really do play nice together?

  2. what behaviour do you observe at the moment that you describe as
    "don't play nicely together"

What you want is for multiple things to run together smoothly.

What that takes is having no code in loop() that hogs execution.
Every function that runs in loop() does a small step then returns to let the others run step of their own. The functions keep track of what step to do next time since that will be taken ASAP, I have made examples showing loop() running > 50 times per millisecond with 3 small functions doing their next steps (mostly a time-check says not yet step! Very Short!) that often, Inputs checked all the time smooth.

You use millis() but if you learn more, using smaller, simpler code then you can revisit your project from an easier direction that will let you add features till the chip is loaded down with no cycles unwasted. For simple things that don't happen at high frequency.. 100 tasks won't overload an Uno as long as none of them hogs the CPU that keeps all of them going.

When you get the picture, practice the techniques. Next small lesson that also needs practixe is Finite State Machines or simply 'state machines' and how they help organize code thinking. Your current project can be turned into tasks written in small side sketches then added as modules to the ongoing project and debugged/made known and safe to add moar.

Start here with millis timers at beginner level. It is Nick Gammon's site.

That method will get you in trouble when an overflow occurs in calculating PumpTimeOut, resulting in the value immediately being less than CurrentTime.

in this tutorial there are a few pictures that illustrate that

and the tutorial shows the basic principle how to replace for-loops (that do hog code-execution until the for-loop has finished its 100 iterations. (or how many iterations the for-loop ever has)

More explanations help.

Learning this is like learning to ride a bicycle. There's different parts that only come together and produce in motion, the front wheel keeps you up!

I had a moment when I ran a loop count and display Hz and got button and led examples looping in the 67K range (IDE 1.6, with IDE 2.1... 55KHz) kinda blew my mind. I was used to using Unix seconds before.

Arduino cooperative multitasking lets us slice milliseconds with small overhead.

So if I'm getting this right, the likely culprit of my three copies of the same function not working when they're running at the same time is that their blocking is weird.
I'm just going split things out and use multiple UNOs because I do have a bunch of UNOs and I don't have enough time to dive into the rabbit hole of learning how to restructure the timing of things, but if a future person makes it to this thread in search of some kind of solution, that's where I'd start.

If you would finally describe what the leds are really doing and how this differs from what you want them to do it would be possible to guide you to solve it.