constexpr uint8_t MAX_STATES {4};
constexpr uint8_t BIT_MASK {0x4};

// Class/struct definitions
class Timer {
public:
  void start() { timeStamp = millis(); }
  bool operator()(const uint32_t duration) { return (millis() - timeStamp >= duration) ? true : false; }

private:
  uint32_t timeStamp {0};
};

namespace TL {
struct Control {
  const uint16_t duration[MAX_STATES];
  const uint8_t ledPin[3];
  const uint8_t mask[MAX_STATES];
};

enum class Phase : uint8_t { red, redYellow, green, yellow, wait };   // Definition of TLPhase
Phase &operator++(Phase &phase) {
  return phase = (phase > Phase::green) ? Phase::red : static_cast<Phase>(static_cast<uint8_t>(phase) + 1);
};

class Device {
public:
  Device(Timer &timer_, const Control &ctrl_, TL::Phase switchLight = TL::Phase::red) : timer {timer_}, ctrl {ctrl_} {}

  void begin() {
    for (auto p : ctrl.ledPin) { pinMode(p, OUTPUT); }
  }

  void switchLight() {
    timer.start();
    idx = static_cast<uint8_t>(pNow);
    pNext = ++pNow;
    pNow = Phase::wait;
    uint8_t i {0};
    for (auto p : ctrl.ledPin) {
      ((ctrl.mask[idx] << i) & BIT_MASK) ? digitalWrite(p, HIGH) : digitalWrite(p, LOW);
      ++i;
    }
  }

  void check() {
    if (timer(ctrl.duration[idx]) == true) { pNow = pNext; }
  }

  Phase phase() const { return pNow; }

private:
  Timer &timer;
  const Control &ctrl;
  Phase pNow;
  Phase pNext;
  uint8_t idx;
};
}   // namespace TL

//
// Global variables, objects
//
Timer timer;
TL::Control tlCtrl {
    {15000, 3000, 15000, 3000}, // Length of the light phase in milliseconds
    {6, 5, 4}, // Pins to which the LEDs are connected
    {4, 6, 1, 2}  // Bit pattern for switching the LEDs
};
TL::Device tLight {timer, tlCtrl};

//
// Function(s)
//
void fstm(TL::Device &tl) {
  switch (tl.phase()) {
    case TL::Phase::red: [[fallthrough]];
    case TL::Phase::redYellow: [[fallthrough]];
    case TL::Phase::green: [[fallthrough]];
    case TL::Phase::yellow: tl.switchLight(); break;
    case TL::Phase::wait: tl.check(); break;
  }
}

//
// Main Program
//
void setup() { tLight.begin(); }

void loop() { fstm(tLight); }
red red-yellow yellow green
15 sec 3 sec 3 sec 15 sec