Project Question: Non Blocking Functions

So Ive been coding a project which involves some LEDs running sequences and a stepper motor. I want both sets of code to run simutaneously, however despite research on non-blocking code - im struggling to get it to work. Would appreicate advice - currently LEDs are based on time intervals but I would prefer them based on motor position but I struggle to get it to run smoothly side by side. Sorry my code is a bit messy as Ive spent hours trying to sort it.

// Libraries
#include <AccelStepper.h>
#include <FastLED.h>
#include <Wire.h> 

// LED No. and Pins
#define NUM_LEDS_A 23 // RH Inlet
#define DATA_PIN_A 1 //
#define NUM_LEDS_B 6 // RH Cylinder
#define DATA_PIN_B 3
#define NUM_LEDS_C 4 // LH Exhaust
#define DATA_PIN_C 5
#define NUM_LEDS_D NUM_LEDS_A  // LH Inlet
#define DATA_PIN_D 2
#define NUM_LEDS_E NUM_LEDS_B  // LH Cylinder
#define DATA_PIN_E 4

// Arrays for LED colors
CRGB ledsA[NUM_LEDS_A];
CRGB ledsB[NUM_LEDS_B];
CRGB ledsC[NUM_LEDS_C];
CRGB ledsD[NUM_LEDS_D];  
CRGB ledsE[NUM_LEDS_E];  

// Solenoid Variables
const int solenoidPin = 7; // Solenoid connected to digital pin 6
unsigned long previousMillis_VALVE = 0;


// Motor interface type for a driver
AccelStepper stepper(AccelStepper::DRIVER, 10, 9); // Step pin 10, direction pin 9

// Switch and Button Pins
const int homeSwitchPin = 25;
const int startButtonPin = 24; // Start button connected to pin 24

// Motor Variables
unsigned long previousMillis_MOTOR = 0; // Stores the last time the motor direction was changed
bool direction = true; // Direction of rotation

const int motorMaxspeedhome = 1000; // DO NOT CHANGE
const int motorAccelerationhome = 1000; // DO NOT CHANGE

float motorMaxspeed = 1900; // Motor max speed - CAN BE ADJUSTED FROM - TO - 

float motorAcceleration = 30000; // Motor acceleration - DO NOT CHANGE
float motorSteps = 5000; // Motor Steps per 1/2 cycle - DO NOT CHANGE

// LED Interval Constants
float Ta = motorMaxspeed/motorAcceleration;
float Sa = Ta * motorMaxspeed/2;
float Sc = motorSteps - (Sa * 2);
float Tc = Sc/motorMaxspeed;

float motorHalfcycle = (((motorSteps - (((motorMaxspeed/motorAcceleration) * motorMaxspeed/2) * 2))/motorMaxspeed) + (2 * (motorMaxspeed/motorAcceleration)) + 0.025) * 1000;
float motorFullcycle = motorHalfcycle * 2;

const int combustionInterval = motorHalfcycle;
int combustionPreviousMillis = -combustionInterval;
const int combustionFlashInterval = motorHalfcycle/12;
int combustionFlashPreviousMillis = 0;

unsigned long previousMillis = 0;  // Stores the last time the LED was updated
const long interval = 100;         // Interval at which to update the LEDs (milliseconds)
int currentLED = 0;                // Index of the current LED to light up or turn off
bool stripBInitialized = false;    // Flag to check if strip B is initialized
bool lightingUp = true;            // Direction of the sequence: true for lighting up, false for turning off
int currentIndex = 0;

unsigned long fadePreviousMillis = 0;
const long fadeInterval = 33;
int fadeIndex = 0;
int totalUpdates = 100; // Total fade updates
int ledOffInterval = 27; // Compression Timing
int combustionFlashCount = 0;
int combustionLEDIndex = NUM_LEDS_B - 1;

// Motor Booleans
bool motorStarted = false;
bool homingComplete = false; // Flag to ensure homing happens only once
bool additionalMoveComplete = false; // Flag to ensure the additional move is done only once
bool motorCycle = false;
bool startLEDCycle = false;
bool Test = false;
bool combustionCycleB = false;
bool Initialize = false;
bool motorCompletedThisCycle = false;

// LED Variables
unsigned long currentMillis = 0; // last time cycle began for RH side LEDS
unsigned long previousmotorMillis = 0; // last time cycle began for LH side LEDS



// LED States
enum class State {
  CombustionRH,
  CompressionLH,
  InletRH,
  ScavengeRH,
  CombustionLH,
  CompressionRH,
  InletLH,
  ScavengeLH,
  ExhaustLH,
  Initialize,
  StripA_Blue,
  StripB_ToBlue,
  StripC_Red,
  TurnOff_AC,
  FadeStripB,
  CombustionSequence,
  Done
};
State currentState = State::Initialize;

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

  pinMode(homeSwitchPin, INPUT_PULLUP);
  pinMode(startButtonPin, INPUT_PULLUP);
  pinMode(solenoidPin, OUTPUT);

  FastLED.addLeds<WS2812B, DATA_PIN_A, GRB>(ledsA, NUM_LEDS_A);
  FastLED.addLeds<WS2812B, DATA_PIN_B, GRB>(ledsB, NUM_LEDS_B);
  FastLED.addLeds<WS2812B, DATA_PIN_C, GRB>(ledsC, NUM_LEDS_C);
  FastLED.addLeds<WS2812B, DATA_PIN_D, GRB>(ledsD, NUM_LEDS_D);  
  FastLED.addLeds<WS2812B, DATA_PIN_E, GRB>(ledsE, NUM_LEDS_E);  
  FastLED.clear();

  fill_solid(ledsA, NUM_LEDS_A, CRGB::Blue); // Strip A Blue
  fill_solid(ledsB, NUM_LEDS_B, CRGB::Red); // Strip B Red
  fill_solid(ledsC, NUM_LEDS_C, CRGB::Red); // Strip C Red
  fill_solid(ledsD, NUM_LEDS_D, CRGB::Red); // Strip D Blue
  fill_solid(ledsE, NUM_LEDS_E, CRGB::Blue); // Strip E Red

  FastLED.show();
}

void loop() {

  // Check if start button is pressed and motor hasn't started yet
  if (digitalRead(startButtonPin) == HIGH && !motorStarted) {
    delay(500); // Debounce delay
    motorStarted = true; // Prevent re-entry
  }

  // Perform homing if it hasn't been done yet and the motor has started
  if (motorStarted && !homingComplete) {
    // Set speed and acceleration for homing
    stepper.setMaxSpeed(motorMaxspeedhome); // max speed for faster homing
    stepper.setAcceleration(motorAccelerationhome); // acceleration for faster homing

    stepper.setSpeed(-motorMaxspeedhome); // speed for homing
    // Homing procedure: move continuously until the home switch is triggered
    while (digitalRead(homeSwitchPin) == HIGH) {
      stepper.runSpeed(); // Move continuously
    }

    stepper.stop(); // Stop the motor once the home switch is triggered
    stepper.setCurrentPosition(0); // Set current position as 0

    // Reset speed and acceleration to normal operation values
    stepper.setMaxSpeed(motorMaxspeed); // Reset max speed
    stepper.setAcceleration(motorAcceleration); // Reset acceleration

    homingComplete = true; // Set homing complete flag
  }
  
  // After homing, move an additional 370 steps
  if (homingComplete && !additionalMoveComplete) {
    stepper.move(370); // Move 370 steps away from the switch
    while (stepper.distanceToGo() != 0) {
      stepper.run(); // Continuously run until the motor reaches the target position
    }
    additionalMoveComplete = true; // Mark the additional move as complete
  }

  // After homing, control the motor based on logic
  if (homingComplete) {
    unsigned long currentmotorMillis;
    if (currentmotorMillis - previousmotorMillis >= 1) {
      previousmotorMillis = currentmotorMillis;
      if (stepper.distanceToGo() == 0) {
        direction = !direction;
        long stepsToMove = direction ? -5000 : 5000;
        stepper.move(stepsToMove);
      }
      stepper.run();
    }
  }
  unsigned long currentMillis = millis();
  switch (currentState) {
    case State::Initialize:
      currentIndex = 0; // Reset index for the first operation
      if (motorStarted && !startLEDCycle) {
        currentState = State::CombustionRH;
        startLEDCycle = true;
      }
      break;

    case State::CombustionRH: //COMBUSTION
      if (currentMillis - previousMillis >= combustionFlashInterval) {
        previousMillis = currentMillis;
        combustionRH();
      }
      break;
    
    case State::InletRH:
      if (currentMillis - previousMillis >= interval && currentIndex < NUM_LEDS_A) {
        previousMillis = currentMillis;
        inletRH();
      } else if (currentIndex >= NUM_LEDS_A && currentIndex >= NUM_LEDS_C) {
        currentIndex = 0; // Reset for the next sequence
        currentState = State::ScavengeLH;
      }
      break;

    case State::ScavengeLH: // NEW AIR PUSHING OLD AIR OUT
      if (currentMillis - previousMillis >= interval && currentIndex < NUM_LEDS_B) {
        previousMillis = currentMillis;
        scavengeLH();
      } else if (currentIndex >= NUM_LEDS_B) {
        currentIndex = 0; // Reset for the next sequence
        currentState = State::ExhaustLH;
      }
      break;

    case State::ExhaustLH: //EXHUAST
      if (currentMillis - previousMillis >= interval && currentIndex < NUM_LEDS_C) {
        previousMillis = currentMillis;
        ledsC[currentIndex] = CRGB::Red;
        FastLED.show();
        currentIndex++;
      } else if (currentIndex >= NUM_LEDS_C) {
        fill_solid(ledsA, NUM_LEDS_A, CRGB::Black); // Turn off strip A
        fill_solid(ledsC, NUM_LEDS_C, CRGB::Black); // Turn off strip C
        FastLED.show();
        currentState = State::CompressionRH;
        totalUpdates = ledOffInterval * NUM_LEDS_B;
        fadeIndex = 0;
      }
      break;

    case State::CompressionRH:
      if (currentMillis - fadePreviousMillis >= fadeInterval) {
        fadePreviousMillis = currentMillis;
        compressionRH();
      }
      if (!motorCompletedThisCycle && stepper.distanceToGo() == 0) {
        motorCompletedThisCycle = true; 
      }

      break;

    case State::Done: //LOOP AGAIN
      currentState = State::Initialize;
      motorStarted = false;
      motorCompletedThisCycle = false;
      break;
  }

  if (motorStarted && !motorCompletedThisCycle) {
    stepper.run();
  }
}

void startLED() {
  startLEDCycle = true;
  currentState = State::CombustionRH;
}

void combustionRH() {
  if (combustionFlashCount < 2 * NUM_LEDS_B) {
    int ledToFlash = combustionLEDIndex - (combustionFlashCount / 2);
    if (ledToFlash >= 0 && ledToFlash < NUM_LEDS_B) {
      for (int j = ledToFlash; j < NUM_LEDS_B; j++) {
        ledsB[j] = (combustionFlashCount % 2 == 0) ? CRGB::Red : CRGB::Black;
      }
    }
    FastLED.show();
    combustionFlashCount++;
    if (combustionFlashCount % 2 == 0 && ledToFlash >= 0) {
      ledsB[ledToFlash] = CRGB::Red; // Keep the LED on red after flashing
    }
  } else {
    currentState = State::InletRH  ; // All LEDs have flashed
  }
}

void inletRH() {
  if (currentIndex < NUM_LEDS_A) {
    ledsA[currentIndex] = CRGB::Blue;
  }

  if (currentIndex == 0) { 
    digitalWrite(solenoidPin, HIGH); // Open the solenoid
  }
  
  // Simultaneously start the sequence on Strip C
  if (currentIndex < NUM_LEDS_C) { // Check to ensure we don't overflow Strip C
    ledsC[currentIndex] = CRGB::Red; // Start Strip C sequence alongside Strip A
  }

  if (currentIndex == 0) {
    fill_solid(ledsB, NUM_LEDS_B, CRGB::Red); // Initialize strip B
  }
  
  FastLED.show();
  currentIndex++;
}

void scavengeLH () {
  if (currentIndex >= 3) {
    ledsC[currentIndex - 3] = CRGB::Black;
  }

  if (currentIndex < NUM_LEDS_C) { // Check to ensure we don't overflow Strip C
    ledsC[currentIndex] = CRGB::Red; // Start Strip C sequence alongside Strip A
  }      
  ledsB[currentIndex] = CRGB::Blue;
  FastLED.show();
  currentIndex++;
}

void compressionRH() {
  if (fadeIndex <= totalUpdates) {
    for (int i = 0; i < NUM_LEDS_B; i++) {
      // Fading logic FOR COMPRESSION
      if (fadeIndex >= i * ledOffInterval && i != NUM_LEDS_B - 1) {
        ledsB[i] = CRGB::Black;
      } else {
        ledsB[i].r = map(fadeIndex, 0, totalUpdates, 0, 80);
        ledsB[i].g = 0;
        ledsB[i].b = map(fadeIndex, 0, totalUpdates, 255, 80);
      }
    }
    FastLED.show();
    fadeIndex++;
  } else {
    // Proceed to the combustion sequence
    currentState = State::CombustionRH;
    combustionLEDIndex = NUM_LEDS_B - 1;
    combustionFlashCount = 0;
  }
}

Take a close look at these and the sections you actually use. To my knowledge both contain blocking sections, In AccelStepper.h
johnwasserKarma: 2000+

Jul 2021post #4

It is definitely NOT safe to call most Accelstepper functions from an ISR.

The 'blocking' functions just calls the non-blocking function until the move is done:

// Blocks until the target position is reached and stopped
void AccelStepper::runToPosition()
{
while (run())
YIELD; // Let system housekeeping occur
}

I think the best way to handle limit switches is to write your own 'non blocking' function instead of using the library function. Polling the limit switch between calls to .run(). On limit, call .stop() and then run to the new position. The 'stop' function sets the destination to the closest point where maximum deceleration can stop. You will need some compliance in your limit switch so the switch will activate before it is too late to stop.

That's not impressive. Cracking an issue needed 2 weeks of reading the project documentation. Solving another home real time problem took a year.....

Tells nothing, What is the problem?

Non-blocking code does not contain any loops.

If you use third party (Arduino...) libraries then check which functions contain loops and consequently should not be called in a non-blocking application.

Sorry I know it wasn’t specific, Okay well I can run the LEDs separately and the motor but not together. I’m currently using Accelstepper. I don’t know how to run it where the motor and LEDs don’t interfere. It’s a Nema 17, running at 400 steps per revolution. I’ve looked up about non blocking code like void functions and case and Millie but can’t get it to work.

Here you go, it's a game changer:
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution

You have the hard work done with your timer for your neopixels... your next step is to use the same timer to step your motor. You know your direction pin, step pin, steps-per-revolution, and desired number of revolutions. You only need to know "when" to move the motor.

Your code might look something like this...

// previously defined hardware

void setup() {
  // previously configured hardware
}

void loop() {
  checkMyTimer();
  if (it is time to move the motor one step)
    stepTheMotor(); // call the one-step function
  if (it is time to change the pixel display)
    changePixels(); // call the pixel display function
}

void stepTheMotor() {
  // intermission... grab some popped corn
}

Here is an example for stepTheMotor() - You will need to generate a square wave with a two-millisecond period (for this example). The square wave can be (1) a toggle pulse (ON/OFF), then pause for the remainder of the interval, or (2) a square wave with an ON period and an OFF period that equals the interval (1ms ON, 1ms OFF). Here is the example with equal periods..

void stepTheMotor() { // the timer interval indicates it is time to step the motor
  stepState = !stepState;  // toggle the state (ON/OFF)
  delay(10); // let the motor arrive at the next step ... optional, slows the motor
  digitalWrite(stepPin, stepState); // step the motor
}

And... here is an example of a timer as a function to be called from loop()... simple, small, modular, any function can use it

void checkMyTimer() {
  unsigned long currentMillis = millis(); // store the current time in milliseconds
  if (currentMillis - previousMillis >= interval) { // compare elapsed time to known interval
    previousMillis = currentMillis; // store last step time
    stepNow = 1; // flag to indicate it is time to step the motor
  }
}

Ask any question. I see you are making a working motor simulation. I look forward to seeing the completion. You have no "RH Exhaust?"

Are you using a Mega2560? ESP32? Using Mega2560.

The first condition in loop() is looking for an UNPRESSED/HIGH button, not a PRESSED/LOW button.

I got it to work. Press HOME and the motor runs (but never "homes" in the simulation). Press "START" and the LEDs start doing their thing. The Relay (Solenoid) energizes after the seventh CombustionRH LED blink (14 cycles). I added an RH Exhaust LEDs, but they are not addressed in the code.

Again... HOME does not "home" in the simulation.

The WOKWI.COM files...

sketch.ino
// Libraries
#include <AccelStepper.h>
#include <FastLED.h>
#include <Wire.h>

// LED No. and Pins
#define NUM_LEDS_A 23 // RH Inlet
#define DATA_PIN_A 8 //
// #define DATA_PIN_A 1 //
#define NUM_LEDS_B 6 // RH Cylinder
#define DATA_PIN_B 3
#define NUM_LEDS_C 4 // RH Exhaust
#define DATA_PIN_C 5
#define NUM_LEDS_D NUM_LEDS_A  // LH Inlet
#define DATA_PIN_D 2
#define NUM_LEDS_E NUM_LEDS_B  // LH Cylinder
#define DATA_PIN_E 4
#define NUM_LEDS_F NUM_LEDS_C  // LH Exhaust ADDED "F" LH EXHAUST
#define DATA_PIN_F 6 // ADDED "F" LH EXHAUST

// Arrays for LED colors
CRGB ledsA[NUM_LEDS_A];
CRGB ledsB[NUM_LEDS_B];
CRGB ledsC[NUM_LEDS_C];
CRGB ledsD[NUM_LEDS_D];
CRGB ledsE[NUM_LEDS_E];
CRGB ledsF[NUM_LEDS_F]; // ADDED "F" LH EXHAUST

// Solenoid Variables
const int solenoidPin = 7; // Solenoid connected to digital pin 6
unsigned long previousMillis_VALVE = 0;


// Motor interface type for a driver
AccelStepper stepper(AccelStepper::DRIVER, 10, 9); // Step pin 10, direction pin 9

// Switch and Button Pins
const int homeSwitchPin = 25;
const int startButtonPin = 24; // Start button connected to pin 24

// Motor Variables
unsigned long previousMillis_MOTOR = 0; // Stores the last time the motor direction was changed
bool direction = true; // Direction of rotation

const int motorMaxspeedhome = 1000; // DO NOT CHANGE
const int motorAccelerationhome = 1000; // DO NOT CHANGE

float motorMaxspeed = 1900; // Motor max speed - CAN BE ADJUSTED FROM - TO -

float motorAcceleration = 30000; // Motor acceleration - DO NOT CHANGE
float motorSteps = 5000; // Motor Steps per 1/2 cycle - DO NOT CHANGE

// LED Interval Constants
float Ta = motorMaxspeed / motorAcceleration;
float Sa = Ta * motorMaxspeed / 2;
float Sc = motorSteps - (Sa * 2);
float Tc = Sc / motorMaxspeed;

float motorHalfcycle = (((motorSteps - (((motorMaxspeed / motorAcceleration) * motorMaxspeed / 2) * 2)) / motorMaxspeed) + (2 * (motorMaxspeed / motorAcceleration)) + 0.025) * 1000;
float motorFullcycle = motorHalfcycle * 2;

const int combustionInterval = motorHalfcycle;
int combustionPreviousMillis = -combustionInterval;
const int combustionFlashInterval = motorHalfcycle / 12;
int combustionFlashPreviousMillis = 0;

unsigned long previousMillis = 0;  // Stores the last time the LED was updated
const long interval = 100;         // Interval at which to update the LEDs (milliseconds)
int currentLED = 0;                // Index of the current LED to light up or turn off
bool stripBInitialized = false;    // Flag to check if strip B is initialized
bool lightingUp = true;            // Direction of the sequence: true for lighting up, false for turning off
int currentIndex = 0;

unsigned long fadePreviousMillis = 0;
const long fadeInterval = 33;
int fadeIndex = 0;
int totalUpdates = 100; // Total fade updates
int ledOffInterval = 27; // Compression Timing
int combustionFlashCount = 0;
int combustionLEDIndex = NUM_LEDS_B - 1;

// Motor Booleans
bool motorStarted = false;
bool homingComplete = false; // Flag to ensure homing happens only once
bool additionalMoveComplete = false; // Flag to ensure the additional move is done only once
bool motorCycle = false;
bool startLEDCycle = false;
bool Test = false;
bool combustionCycleB = false;
bool Initialize = false;
bool motorCompletedThisCycle = false;

// LED Variables
unsigned long currentMillis = 0; // last time cycle began for RH side LEDS
unsigned long previousmotorMillis = 0; // last time cycle began for LH side LEDS

// LED States
enum class State {
  CombustionRH,
  CompressionLH,
  InletRH,
  ScavengeRH,
  CombustionLH,
  CompressionRH,
  InletLH,
  ScavengeLH,
  ExhaustLH,
  Initialize,
  StripA_Blue,
  StripB_ToBlue,
  StripC_Red,
  TurnOff_AC,
  FadeStripB,
  CombustionSequence,
  Done
};
State currentState = State::Initialize;

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

  pinMode(homeSwitchPin, INPUT_PULLUP);
  pinMode(startButtonPin, INPUT_PULLUP);
  pinMode(solenoidPin, OUTPUT);

  FastLED.addLeds<WS2812B, DATA_PIN_A, GRB>(ledsA, NUM_LEDS_A);
  FastLED.addLeds<WS2812B, DATA_PIN_B, GRB>(ledsB, NUM_LEDS_B);
  FastLED.addLeds<WS2812B, DATA_PIN_C, GRB>(ledsC, NUM_LEDS_C);
  FastLED.addLeds<WS2812B, DATA_PIN_D, GRB>(ledsD, NUM_LEDS_D);
  FastLED.addLeds<WS2812B, DATA_PIN_E, GRB>(ledsE, NUM_LEDS_E);
  FastLED.addLeds<WS2812B, DATA_PIN_F, GRB>(ledsF, NUM_LEDS_F); // ADDED "F" LH EXHAUST
  FastLED.clear();

  fill_solid(ledsA, NUM_LEDS_A, CRGB::Blue); // Strip A Blue
  fill_solid(ledsB, NUM_LEDS_B, CRGB::Red); // Strip B Red
  fill_solid(ledsC, NUM_LEDS_C, CRGB::Red); // Strip C Red
  fill_solid(ledsD, NUM_LEDS_D, CRGB::Red); // Strip D Red
  fill_solid(ledsE, NUM_LEDS_E, CRGB::Blue); // Strip E Red
  fill_solid(ledsF, NUM_LEDS_F, CRGB::Blue); // Strip F Blue // ADDED "F" LH EXHAUST

  FastLED.show();
}

void loop() {

  // Check if start button is pressed and motor hasn't started yet
  // if (digitalRead(startButtonPin) == HIGH && !motorStarted) {
  if (digitalRead(startButtonPin) == LOW && !motorStarted) {
    delay(500); // Debounce delay
    motorStarted = true; // Prevent re-entry
  }

  // Perform homing if it hasn't been done yet and the motor has started
  if (motorStarted && !homingComplete) {
    // Set speed and acceleration for homing
    stepper.setMaxSpeed(motorMaxspeedhome); // max speed for faster homing
    stepper.setAcceleration(motorAccelerationhome); // acceleration for faster homing

    stepper.setSpeed(-motorMaxspeedhome); // speed for homing
    // Homing procedure: move continuously until the home switch is triggered
    while (digitalRead(homeSwitchPin) == HIGH) {
      stepper.runSpeed(); // Move continuously
    }

    stepper.stop(); // Stop the motor once the home switch is triggered
    stepper.setCurrentPosition(0); // Set current position as 0

    // Reset speed and acceleration to normal operation values
    stepper.setMaxSpeed(motorMaxspeed); // Reset max speed
    stepper.setAcceleration(motorAcceleration); // Reset acceleration

    homingComplete = true; // Set homing complete flag
  }

  // After homing, move an additional 370 steps
  if (homingComplete && !additionalMoveComplete) {
    stepper.move(370); // Move 370 steps away from the switch
    while (stepper.distanceToGo() != 0) {
      stepper.run(); // Continuously run until the motor reaches the target position
    }
    additionalMoveComplete = true; // Mark the additional move as complete
  }

  // After homing, control the motor based on logic
  if (homingComplete) {
    unsigned long currentmotorMillis;
    if (currentmotorMillis - previousmotorMillis >= 1) {
      previousmotorMillis = currentmotorMillis;
      if (stepper.distanceToGo() == 0) {
        direction = !direction;
        long stepsToMove = direction ? -5000 : 5000;
        stepper.move(stepsToMove);
      }
      stepper.run();
    }
  }
  unsigned long currentMillis = millis();
  switch (currentState) {
    case State::Initialize:
      currentIndex = 0; // Reset index for the first operation
      if (motorStarted && !startLEDCycle) {
        currentState = State::CombustionRH;
        startLEDCycle = true;
      }
      break;

    case State::CombustionRH: //COMBUSTION
      if (currentMillis - previousMillis >= combustionFlashInterval) {
        previousMillis = currentMillis;
        combustionRH();
      }
      break;

    case State::InletRH:
      if (currentMillis - previousMillis >= interval && currentIndex < NUM_LEDS_A) {
        previousMillis = currentMillis;
        inletRH();
      } else if (currentIndex >= NUM_LEDS_A && currentIndex >= NUM_LEDS_C) {
        currentIndex = 0; // Reset for the next sequence
        currentState = State::ScavengeLH;
      }
      break;

    case State::ScavengeLH: // NEW AIR PUSHING OLD AIR OUT
      if (currentMillis - previousMillis >= interval && currentIndex < NUM_LEDS_B) {
        previousMillis = currentMillis;
        scavengeLH();
      } else if (currentIndex >= NUM_LEDS_B) {
        currentIndex = 0; // Reset for the next sequence
        currentState = State::ExhaustLH;
      }
      break;

    case State::ExhaustLH: //EXHUAST
      if (currentMillis - previousMillis >= interval && currentIndex < NUM_LEDS_C) {
        previousMillis = currentMillis;
        ledsC[currentIndex] = CRGB::Red;
        FastLED.show();
        currentIndex++;
      } else if (currentIndex >= NUM_LEDS_C) {
        fill_solid(ledsA, NUM_LEDS_A, CRGB::Black); // Turn off strip A
        fill_solid(ledsC, NUM_LEDS_C, CRGB::Black); // Turn off strip C
        FastLED.show();
        currentState = State::CompressionRH;
        totalUpdates = ledOffInterval * NUM_LEDS_B;
        fadeIndex = 0;
      }
      break;

    case State::CompressionRH:
      if (currentMillis - fadePreviousMillis >= fadeInterval) {
        fadePreviousMillis = currentMillis;
        compressionRH();
      }
      if (!motorCompletedThisCycle && stepper.distanceToGo() == 0) {
        motorCompletedThisCycle = true;
      }
      break;

    case State::Done: //LOOP AGAIN
      currentState = State::Initialize;
      motorStarted = false;
      motorCompletedThisCycle = false;
      break;
  }

  if (motorStarted && !motorCompletedThisCycle) {
    stepper.run();
  }
}

void startLED() {
  startLEDCycle = true;
  currentState = State::CombustionRH;
}

void combustionRH() {
  if (combustionFlashCount < 2 * NUM_LEDS_B) {
    int ledToFlash = combustionLEDIndex - (combustionFlashCount / 2);
    if (ledToFlash >= 0 && ledToFlash < NUM_LEDS_B) {
      for (int j = ledToFlash; j < NUM_LEDS_B; j++) {
        ledsB[j] = (combustionFlashCount % 2 == 0) ? CRGB::Red : CRGB::Black;
      }
    }
    FastLED.show();
    combustionFlashCount++;
    if (combustionFlashCount % 2 == 0 && ledToFlash >= 0) {
      ledsB[ledToFlash] = CRGB::Red; // Keep the LED on red after flashing
    }
  } else {
    currentState = State::InletRH  ; // All LEDs have flashed
  }
}

void inletRH() {
  if (currentIndex < NUM_LEDS_A) {
    ledsA[currentIndex] = CRGB::Blue;
  }

  if (currentIndex == 0) {
    digitalWrite(solenoidPin, HIGH); // Open the solenoid
  }

  // Simultaneously start the sequence on Strip C
  if (currentIndex < NUM_LEDS_C) { // Check to ensure we don't overflow Strip C
    ledsC[currentIndex] = CRGB::Red; // Start Strip C sequence alongside Strip A
  }

  if (currentIndex == 0) {
    fill_solid(ledsB, NUM_LEDS_B, CRGB::Red); // Initialize strip B
  }

  FastLED.show();
  currentIndex++;
}

void scavengeLH () {
  if (currentIndex >= 3) {
    ledsC[currentIndex - 3] = CRGB::Black;
  }

  if (currentIndex < NUM_LEDS_C) { // Check to ensure we don't overflow Strip C
    ledsC[currentIndex] = CRGB::Red; // Start Strip C sequence alongside Strip A
  }
  ledsB[currentIndex] = CRGB::Blue;
  FastLED.show();
  currentIndex++;
}

void compressionRH() {
  if (fadeIndex <= totalUpdates) {
    for (int i = 0; i < NUM_LEDS_B; i++) {
      // Fading logic FOR COMPRESSION
      if (fadeIndex >= i * ledOffInterval && i != NUM_LEDS_B - 1) {
        ledsB[i] = CRGB::Black;
      } else {
        ledsB[i].r = map(fadeIndex, 0, totalUpdates, 0, 80);
        ledsB[i].g = 0;
        ledsB[i].b = map(fadeIndex, 0, totalUpdates, 255, 80);
      }
    }
    FastLED.show();
    fadeIndex++;
  } else {
    // Proceed to the combustion sequence
    currentState = State::CombustionRH;
    combustionLEDIndex = NUM_LEDS_B - 1;
    combustionFlashCount = 0;
  }
}
diagram.json
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-mega", "id": "mega", "top": -37.8, "left": 15.6, "attrs": {} },
    {
      "type": "wokwi-led-ring",
      "id": "ring1",
      "top": -300.67,
      "left": -0.66,
      "attrs": { "pixels": "23" }
    },
    {
      "type": "wokwi-led-ring",
      "id": "ring2",
      "top": -256,
      "left": 21.67,
      "attrs": { "pixels": "6" }
    },
    {
      "type": "wokwi-led-ring",
      "id": "ring3",
      "top": -253.56,
      "left": 94.89,
      "attrs": { "pixels": "4" }
    },
    {
      "type": "wokwi-led-ring",
      "id": "ring4",
      "top": -300.67,
      "left": 191.34,
      "attrs": { "pixels": "23" }
    },
    {
      "type": "wokwi-led-ring",
      "id": "ring5",
      "top": -256,
      "left": 213.67,
      "attrs": { "pixels": "6" }
    },
    {
      "type": "wokwi-led-ring",
      "id": "ring6",
      "top": -253.56,
      "left": 286.89,
      "attrs": { "pixels": "4" }
    },
    { "type": "wokwi-gnd", "id": "gnd1", "top": -67.2, "left": 9, "attrs": {} },
    { "type": "wokwi-vcc", "id": "vcc1", "top": -133.64, "left": 9.6, "attrs": {} },
    { "type": "wokwi-relay-module", "id": "relay1", "top": -105.4, "left": 393.6, "attrs": {} },
    {
      "type": "wokwi-stepper-motor",
      "id": "stepper1",
      "top": -307.21,
      "left": 454.62,
      "attrs": { "size": "8" }
    },
    { "type": "wokwi-a4988", "id": "drv1", "top": -206.4, "left": 379.2, "attrs": {} },
    { "type": "wokwi-vcc", "id": "vcc2", "top": -344.84, "left": 441.6, "attrs": {} },
    { "type": "wokwi-gnd", "id": "gnd2", "top": -249.6, "left": 402.6, "attrs": {} },
    {
      "type": "wokwi-pushbutton",
      "id": "btn1",
      "top": 102.2,
      "left": 412.8,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-pushbutton",
      "id": "btn2",
      "top": -22.6,
      "left": 412.8,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-logo",
      "id": "logo1",
      "top": 67.2,
      "left": 76.8,
      "rotate": 180,
      "attrs": { "color": "magenta" }
    },
    {
      "type": "wokwi-text",
      "id": "legendservo1",
      "top": -9.6,
      "left": 489.6,
      "attrs": { "text": "START" }
    },
    {
      "type": "wokwi-text",
      "id": "legendservo2",
      "top": 115.2,
      "left": 489.6,
      "attrs": { "text": "HOME" }
    }
  ],
  "connections": [
    [ "gnd1:GND", "ring1:GND", "black", [ "v-9.6", "h86.4" ] ],
    [ "gnd1:GND", "ring3:GND", "black", [ "v-9.6", "h124.8" ] ],
    [ "gnd1:GND", "ring4:GND", "black", [ "v-9.6", "h278.4" ] ],
    [ "gnd1:GND", "ring5:GND", "black", [ "v-9.6", "h288" ] ],
    [ "gnd1:GND", "ring6:GND", "black", [ "v-9.6", "h307.2" ] ],
    [ "gnd1:GND", "ring2:GND", "black", [ "v-9.6", "h38.4" ] ],
    [ "vcc1:VCC", "ring2:VCC", "red", [ "v19.2", "h38.4" ] ],
    [ "vcc1:VCC", "ring1:VCC", "red", [ "v19.2", "h67.2" ] ],
    [ "vcc1:VCC", "ring3:VCC", "red", [ "v19.2", "h105.6" ] ],
    [ "vcc1:VCC", "ring5:VCC", "red", [ "v19.2", "h268.8" ] ],
    [ "vcc1:VCC", "ring4:VCC", "red", [ "v19.2", "h259.2" ] ],
    [ "vcc1:VCC", "ring6:VCC", "red", [ "v19.2", "h278.4" ] ],
    [ "mega:7", "relay1:IN", "green", [ "v0" ] ],
    [ "drv1:2B", "stepper1:A+", "green", [ "h0" ] ],
    [ "stepper1:A-", "drv1:2A", "green", [ "v0" ] ],
    [ "drv1:1A", "stepper1:B-", "green", [ "h0" ] ],
    [ "stepper1:B+", "drv1:1B", "green", [ "v0" ] ],
    [ "vcc2:VCC", "drv1:VMOT", "red", [ "v0" ] ],
    [ "gnd2:GND", "drv1:GND.2", "black", [ "v-9.6", "h28.8", "v67.2" ] ],
    [ "mega:9", "drv1:DIR", "green", [ "v-19.2", "h191.3", "v-76.8" ] ],
    [ "mega:10", "drv1:STEP", "green", [ "v-28.8", "h191.7", "v-86.4" ] ],
    [ "mega:24", "btn1:1.l", "orange", [ "h-11.8", "v116.15" ] ],
    [ "mega:GND.5", "btn1:2.l", "black", [ "v0.95", "h26.2", "v-0.2" ] ],
    [ "vcc2:VCC", "drv1:VDD", "red", [ "v0" ] ],
    [ "gnd2:GND", "drv1:GND.1", "black", [ "v-9.6", "h28.8", "v124.8" ] ],
    [ "drv1:SLEEP", "drv1:RESET", "green", [ "h-19.2", "v-9.6" ] ],
    [ "mega:3", "ring2:DIN", "green", [ "v-86.4", "h-109.7" ] ],
    [ "mega:5", "ring3:DIN", "green", [ "v-76.8", "h-90.7" ] ],
    [ "mega:2", "ring4:DIN", "green", [ "v-86.4", "h24.8" ] ],
    [ "mega:4", "ring5:DIN", "green", [ "v-115.2", "h24.6" ] ],
    [ "mega:6", "ring6:DIN", "green", [ "v-105.6", "h110.8" ] ],
    [ "mega:8", "ring1:DIN", "green", [ "v-67.2", "h-87" ] ],
    [ "mega:25", "btn2:1.l", "orange", [ "v0" ] ],
    [ "mega:GND.5", "btn2:2.l", "black", [ "v0.95", "h16.6", "v-125" ] ]
  ],
  "dependencies": {}
}

p.s. You have no CompressionLH, or ScavengeRH, or CombustionLH, or InletLH, or ExhaustLH, or StripA_Blue, or StripB_ToBlue, or StripC_Red, or TurnOff_AC, or FadeStripB, or CombustionSequence function. Most are only enumerations.

stepper.run does not belong here and may be the problem.
It needs to be placed so it is called on every loop().
It returns true if it has NOT reached the destination.

The stepper.move command works relative to the current position which can be problematic. The stepper.moveTo command works relative to the home position and is much easier to keep track of the steppers position.

This allows homing only once during setup(). you really should not need to ever rehome the stepper.

All this reduces the above code to:


  // After homing, control the motor based on logic
  if (!stepper.run) {
    long targetPosition = direction ? pointA : pointB; 
    stepper.moveTo(targetPosition);
  } // End if()
  stepper.run;
 //..........more code........
}// End loop()

Notice how that changes the perspective from "how many steps to move" to
"go to this position".

I would need to know more about how you wish to the stepper to interact with the LED's to help with that.

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