// Pin mappings
const int ledPins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 46, 47, 48, 49};
const int buttonPins[] = {22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37};
const int blueLedPin = 52;
const int yellowLedPin = 53;
const int purpleLedPin = 44; // New purple LED pin
const int whiteLedPin = 45; // New white LED pin
const int sequenceButtonPins[] = {A2, A3, A4, A5}; // New sequence selection buttons
const int tempoControlPin = A0;
const int startStopSwitchPin = 50; // Switch pin for start/stop control
const int externalClockPin = 42; // Pin for external clock pulses
const int resetPin = 43; // Pin for reset pulses
constexpr int reverseSwitchPin = 39; // Reverse playback switch pin
constexpr int randomModeSwitchPin = 38; // Random mode switch pin
constexpr uint8_t numSteps = 16; // Number of Steps
constexpr uint8_t numSequences = 4; // Number of Sequences
constexpr unsigned long debounceDelay = 20;
constexpr unsigned long blinkDuration = 50;
constexpr int minBPM = 30;
constexpr int maxBPM = 255;
// Tempo and sequence control
unsigned long stepDurationMs = 300;
unsigned long previousStepTime = 0;
uint8_t currentStep = 0;
bool sequencerRunning = false;
bool randomMode = false; // Track the random mode state
// Sequence storage (4 sequences)
bool storedStates[numSequences][numSteps] = {{false}, {false}, {false}, {false}};
// Button debounce and state tracking
unsigned long lastDebounceTimes[numSteps] = {0};
int buttonStates[numSteps] = {HIGH};
int lastButtonStates[numSteps] = {HIGH};
// LED blink control
unsigned long blinkStartTime[numSequences] = {0, 0, 0, 0};
bool isBlinking[numSequences] = {false, false, false, false};
// External clock and reset state tracking
int lastExternalClockState = HIGH;
int lastResetState = HIGH;
uint8_t activeSequence = 0; // Track the active sequence
void setup() {
for (uint8_t i = 0; i < numSteps; i++) {
pinMode(ledPins[i], OUTPUT);
pinMode(buttonPins[i], INPUT_PULLUP);
}
pinMode(blueLedPin, OUTPUT);
pinMode(yellowLedPin, OUTPUT);
pinMode(purpleLedPin, OUTPUT); // Initialize new LED pins
pinMode(whiteLedPin, OUTPUT);
for (uint8_t i = 0; i < numSequences; i++) {
pinMode(sequenceButtonPins[i], INPUT_PULLUP);
}
pinMode(startStopSwitchPin, INPUT_PULLUP);
pinMode(tempoControlPin, INPUT);
pinMode(externalClockPin, INPUT_PULLUP);
pinMode(resetPin, INPUT_PULLUP);
pinMode(reverseSwitchPin, INPUT_PULLUP);
pinMode(randomModeSwitchPin, INPUT_PULLUP);
randomSeed(analogRead(A1)); // Initialize random seed
}
void loop() {
const unsigned long currentTime = millis();
// Cache pin states
const bool startStopState = digitalRead(startStopSwitchPin);
const int externalClockState = digitalRead(externalClockPin);
const int resetState = digitalRead(resetPin);
const bool reversePlayback = digitalRead(reverseSwitchPin) == LOW;
randomMode = digitalRead(randomModeSwitchPin) == HIGH; // Update random mode state
// Update sequencer state
sequencerRunning = startStopState == HIGH;
updateTempo(); // Update stepInterval based on the potentiometer
handleSequenceButtons(currentTime); // Handle sequence selection buttons
handleButtons(activeSequence, currentTime);
// Handle external clock and reset pulses ONLY if startStopSwitchPin is LOW
if (!sequencerRunning) {
handleExternalClock(currentTime, externalClockState, reversePlayback, randomMode);
handleReset(resetState);
}
// Only update the sequencer if it's running or in external clock mode
if (sequencerRunning) {
updateSequencer(currentTime, reversePlayback, randomMode);
}
updateDisplay(activeSequence);
updateTriggerLeds(currentTime);
}
void updateTempo() {
int potValue = analogRead(tempoControlPin);
int bpm = map(potValue, 0, 1023, minBPM, maxBPM);
stepDurationMs = static_cast<unsigned long>((60000.0f / bpm) / 4);
}
void handleSequenceButtons(unsigned long currentTime) {
for (uint8_t i = 0; i < numSequences; i++) {
if (digitalRead(sequenceButtonPins[i]) == LOW) {
activeSequence = i;
delay(debounceDelay); // Simple debounce
break;
}
}
}
void handleButtons(uint8_t activeSequence, unsigned long currentTime) {
for (uint8_t i = 0; i < numSteps; i++) {
const int reading = digitalRead(buttonPins[i]);
if ((currentTime - lastDebounceTimes[i]) > debounceDelay && reading != buttonStates[i]) {
buttonStates[i] = reading;
if (buttonStates[i] == LOW) {
storedStates[activeSequence][i] = !storedStates[activeSequence][i];
}
}
lastButtonStates[i] = reading;
}
}
void updateStep(bool reversePlayback, bool randomMode) {
if (randomMode) {
currentStep = random(numSteps); // Jump to a random step
} else if (reversePlayback) {
currentStep = (currentStep == 0) ? (numSteps - 1) : (currentStep - 1); // Reverse playback
} else {
currentStep = (currentStep + 1) % numSteps; // Normal playback
}
}
void updateSequencer(unsigned long currentTime, bool reversePlayback, bool randomMode) {
if (currentTime - previousStepTime >= stepDurationMs) {
updateStep(reversePlayback, randomMode);
previousStepTime = currentTime;
for (uint8_t i = 0; i < numSequences; i++) {
if (storedStates[i][currentStep]) {
blinkStartTime[i] = currentTime;
isBlinking[i] = true;
digitalWrite(getTriggerLedPin(i), HIGH);
}
}
}
}
void updateDisplay(uint8_t activeSequence) {
const bool* states = storedStates[activeSequence];
for (uint8_t i = 0; i < numSteps; i++) {
digitalWrite(ledPins[i], (i == currentStep) ? !states[i] : states[i]);
}
}
void updateTriggerLeds(unsigned long currentTime) {
for (uint8_t i = 0; i < numSequences; i++) {
if (isBlinking[i] && (currentTime - blinkStartTime[i] >= blinkDuration)) {
digitalWrite(getTriggerLedPin(i), LOW);
isBlinking[i] = false;
}
}
}
void handleExternalClock(unsigned long currentTime, int externalClockState, bool reversePlayback, bool randomMode) {
if (externalClockState == LOW && lastExternalClockState == HIGH) {
updateStep(reversePlayback, randomMode);
previousStepTime = currentTime;
for (uint8_t i = 0; i < numSequences; i++) {
if (storedStates[i][currentStep]) {
blinkStartTime[i] = currentTime;
isBlinking[i] = true;
digitalWrite(getTriggerLedPin(i), HIGH);
}
}
}
lastExternalClockState = externalClockState;
}
void handleReset(int resetState) {
if (resetState == LOW && lastResetState == HIGH) {
currentStep = 0;
previousStepTime = millis();
}
lastResetState = resetState;
}
int getTriggerLedPin(uint8_t sequence) {
switch (sequence) {
case 0: return blueLedPin;
case 1: return yellowLedPin;
case 2: return purpleLedPin;
case 3: return whiteLedPin;
default: return blueLedPin;
}
}