Multithreading and doing multiple things at once

Hi all,

@BulldogLowell recently helped me out with a problem of mine, and I thought that I should share the code with others, because you can learn a lot from it. This code demonstrates how to do multiple things at once. This code controls 2 stepper motors, and an lcd, and 2 buttons. After you press a button, the stepper motor runs, but after that, there is a 10 second ‘cooldown’ so you can’t spam the button(this is for a halloween candy machine I’m making). While the one motor is ‘cooling down’ you can still press the other button, and you can have two cooldowns going at once. You can see both of the cooldowns counting down on the lcd which is pretty cool. This demonstrates how you can use millis() to multi-thread and create unblocking code. The code is set up in an array, so the one button, motor and lcd are all controlled by one section of the array.

    #include <LiquidCrystal.h>

    #include <Stepper.h>

    const int stepsUp = 240;  // change this to fit the number of steps per revolution up
    const int stepsDown = 230;  // change this to fit the number of steps per revolution down

    LiquidCrystal lcd(B0, B1, B2, B3, B4, B5);

    void updateLCD(int remSec);

    struct Dispenser {
      Dispenser(int pin, int lstate, Stepper* stpr){
          buttonPin = pin;
          lastState = lstate;
          stepper = stpr;
          callBack = new Timer(1800, [this](){stepper->step(-stepsUp);}, true);
      int buttonPin;
      int lastState;
      Stepper* stepper;
      bool lockout;
      uint32_t lockoutStart;
      uint32_t lastSecond;
      Timer* callBack;

      void dispenseCandy()

    Stepper stepperLeft(200, 3, 4, 5, 6);
    Stepper stepperRight(200, C0, C1, C2, C3);
    Dispenser dispenser[] = {{D0, 1, &stepperLeft}, {C5, 1, &stepperRight}};

    void setup(void)
      lcd.begin(16, 2);
      lcd.print("Candy  Dispenser");
      for (auto disp : dispenser)
        pinMode(disp.buttonPin, INPUT);

    void loop(void)
      int i = 0;
      for (auto& disp : dispenser)
        if (!disp.lockout)
          int buttonState = digitalRead(disp.buttonPin);
          if (buttonState != disp.lastState)
            if (disp.lastState == true)
              disp.lockout = true;
              disp.lockoutStart = millis();
          disp.lastState = buttonState;
          int remainingSeconds = 10 - (millis() - disp.lockoutStart) / 1000;
          if (!remainingSeconds)
            disp.lockout = false;
            lcd.setCursor(0, i);
            lcd.print("READY 2 DISPENSE");
            Serial.println("READY 2 DISPENSE");
          else if (remainingSeconds != disp.lastSecond)
            updateLCD(remainingSeconds, i);
            disp.lastSecond = remainingSeconds;

    void updateLCD(int remSec, int row)
      lcd.setCursor(0, row);
      char printBuffer[17];
      sprintf(printBuffer, "Locked %6dsec", remSec);