Eurorack DIY sequencer misbehaving

Brethren I seek your assistance, for I have lost all my hair.

I am building a DIY sequencer from,

And running into issues with its behaviour.

I have rebuild the PCB twice, tried two different nano's, no shorts or dry solder joints and checked all the components, but still the problem persists.

The colours are all out of whack and it doesn't scroll the selection as intended. It's feral.....

I didn't have any issues with the sketch or uploading it to the nano's.

Any help or ideas as to why it's acting so strangely would be much appreciated.


// ---------------------------------------------------------------------------------------------------------------------------
// Code written by David Wieland aka Datulab Tech. Feel free to use this in your own projects and improve upon it. If you make
// something cool, please let me know. This code has been written specifically for the Arduino nano and RGBW Neopixel LEDs. 
// If you want to use a differetn configuration, make sure to update the code. 
// C++ is not my main programming language, so please excuse if the code could be a bit nicer in some areas. 
// If you have any questions, make sure to check out the video on my YouTube channel about this.
// If there still is anything, write me a message to info@datulab.tech
// ---------------------------------------------------------------------------------------------------------------------------

#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>

//Initializing the LEDs
Adafruit_NeoPixel strip = Adafruit_NeoPixel(16, 13, NEO_GRBW + NEO_KHZ800);

//Settings
#define whiteBrightness 15
#define voicePin A2
#define bpmPin A1
#define patternPin A0
#define syncPin A7
#define numPatterns 5                           //number of patterns including one random
#define patternChangeDuration 1000              //how long pattern change is displayed
#define minBpm 20                               //sets minimum bpm
#define bpmScale 3                              //ratio bpm above min is scaled with, at 3 bpm scales from 20 - 360
#define trigDuration 10                         //time in ms that trigger is high

//Switch matrix variables
byte inputs[] = {6, 7, 8, 9, 10};
const byte inCount = sizeof(inputs) / sizeof(inputs[0]);
byte outputs[] = {2, 3, 4, 5};
const byte outCount = sizeof(outputs) / sizeof(outputs[0]);

const byte outputPins[] = {17, 18, 19, 20};

//State variables
bool lengthSelect;
bool trig;
bool sync;
int bpm = 120;
byte voice = 0;
byte pattern = 0;
bool beat[4 * numPatterns][16];                 //[4 voices numPatterns (minus random pattern) patterns][16 beats] true for hit false for no hit
byte lengths[4 * (numPatterns - 1)];            //length 1-15 of the voices
byte gateLength = 14;                           //length of the gate output relative to the beat length (0-15)

//Temporary variables
byte pos;
byte saveSlot;
bool stateChanged;
uint32_t currentColor; 
bool isActive[16];
int patternPot = 1025;
int previousPatternPot;
int voicePot;
int bpmPot;
bool gateChange;
bool patternChange;
bool syncHigh;

//Timing variables
unsigned long currentTime;                      //saves current time
unsigned long previousBeat;                     //time of last beat change
unsigned long previousPatternTime;                  //time of patternChange engage
long beatDuration;
long gateDuration;
byte currentBeat[numPatterns];

//Colors
byte voiceColor[4][4] = {{30, 10, 0, 0},
                         {50, 0, 2, 0},
                         {5, 0, 30, 0},
                         {0, 25, 10, 0}};


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Initial Setup ----------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------
void setup() {
  
  for(int i=0; i<outCount; i++){                //Initializes all the switch matrix pins
    pinMode(outputs[i],OUTPUT);
    digitalWrite(outputs[i],HIGH);
  }
  for(int i=0; i<inCount; i++){
    pinMode(inputs[i],INPUT_PULLUP);
  }

  pinMode(syncPin, INPUT);
  pinMode(voicePin, INPUT);
  pinMode(bpmPin, INPUT);
  pinMode(patternPin, INPUT);
  
  Serial.begin(9600);                           //Establishes connection to computer for debuging
  Serial.println("Connected");

  strip.begin();                                //Initializes the LEDs and displays light show
  for(int i = 0; i < 16; i++){
    strip.setPixelColor(i, i * 4, 40 - 2 * i, 20 - i, 0);
    strip.show();
    delay(50);
  }
                                                //reading variables from EEPROM
  int value = 0;                                //overcomplicated way of finding the next save location
  int previousValue = 0;
  int startAddress = 64;
  
  for(int i = 0; i < 16; i++){                  //finds newest starting address by comparing first byte of all locations
    value = EEPROM.read(i * 64);
    if(value == previousValue + 1 || previousValue == 255 || (i == 0 && value != 0)){
      previousValue = value;
      startAddress = i * 64;
      
    }
  }

  gateLength = EEPROM.read(startAddress + 1);   //reads the gate length

  for(int pat = 0; pat < 4; pat++){             //the 2 nested for loops iterate through the patterns and voices
    for(int voi = 0; voi < 4; voi++){
      
      int voiceStart = startAddress + 2 + 3 * (voi + 4 * pat); //starting address of voice
      
      int beat1 = EEPROM.read(voiceStart);      //the 2 integers are converted back to 16 bool values
      for(int i = 0; i < 8; i++){
        if(beat1 / (1 << (7 - i)) > 0){
          beat[voi + 4 * pat][7 - i] = true;
          beat1 = beat1 - (1 << (7 - i));
        }else{
          beat[voi + 4 * pat][7 - i] = false;
        }
      }

      int beat2 = EEPROM.read(voiceStart + 1);
      for(int i = 0; i < 8; i++){
        if(beat2 / (1 << (7 - i)) > 0){
          beat[voi + 4 * pat][15 - i] = true;
          beat2 = beat2 - (1 << (7 - i));
        }else{
          beat[voi + 4 * pat][15 - i] = false;
        }
      }
      
      lengths[voi + 4 * pat] = EEPROM.read(voiceStart + 2); //reading the length value
    }
  }
}


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Reading the switches ---------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------


void readSwitches(){                              //Reading the switches and updating the beat, lengths, lengthSelect, gate, and sync variables with the current switch states
  saveSlot = voice + 4 * pattern;
  
  for (int i = 0; i < 4; i++){                    //iterates through the 4 outputs
    digitalWrite(outputs[i],LOW);
    delayMicroseconds(5);
      
    for(int j = 0; j < 5; j++){                   //iterates throught the 5 inputs
      bool state;                                 //state of current switch
      if(digitalRead(inputs[j])){
        state = false;
      }else{
        state = true;
      }
      pos = 4 * j + i;                            //current switch number
      
      if(j < 4 && voice != 4){                    //if on one of the beat switches
        if(lengthSelect && state){                //if in length selecting mode
          lengths[saveSlot] = pos;
          stateChanged = true;
        }                                         //otherwise if switch is newly engaged
        else if(state && !isActive[pos]){
          beat[saveSlot][pos] = !beat[saveSlot][pos];
          isActive[pos] = true;
          stateChanged = true;
        }                                         //resetting isActive if switch newly disengaged
        else if(!state && isActive[pos]){
          isActive[pos] = false;
        }
      }
      
      else if (j == 4){                           //if on one of the settings switches
        switch(i){
          case 0:
            lengthSelect = state;
          case 1:
            trig = state;
          case 2:
            sync = state;
        }
      }
    }
    digitalWrite(outputs[i],HIGH);
    delayMicroseconds(5);
  }
}


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Reading the Pots -------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------

void readPots(){                                  //Reads pots and updates variables bpm, voice, pattern, and gate length with the current pot positions
  patternPot = analogRead(patternPin);            //reads pots
  bpmPot = analogRead(bpmPin);
  voicePot = analogRead(voicePin);
  
  if(patternPot < previousPatternPot - 5 || patternPot > previousPatternPot + 5){  //if pattern pot has been changed
    if(previousPatternPot != 1025){
      patternChange = true;                       //patternChange will switch LEDs to display pattern or gate length
    }
    previousPatternTime = millis();
    if(lengthSelect){                             //if in lenghth selecting mode
      gateLength = patternPot / 48;               //sets gateLength to a 0-15 number according to pattern pot
      gateChange = true;
    }
    else{                                         //if in normal operating mode
      pattern = patternPot * numPatterns / 1024;  //updates the pattern with pos of pattern pot
    }
    previousPatternPot = patternPot;
  }

  beatDuration = 60000 / (bpm * 4);               //updates beatDuration with current bpm value
  gateDuration = beatDuration * gateLength / 16;  //updates gateDuration with current beatDuration and gateLength
  bpm = minBpm + bpmPot / bpmScale;
  voice = voicePot * 5 / 1024;
}


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Updating the LEDs ------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------

void displayLEDs(){
  if(lengthSelect && voice != 4){                 //if length switch is active
    if(patternChange){                            //gate length selection mode, represents relative gate length with red leds
      for(int i = 0; i < 16; i++){
        if(i <= gateLength){
          strip.setPixelColor(i, 50, 0, 0, 0);
        }else{
          strip.setPixelColor(i, 0, 0, 0, 0);
        }
      }
    }
    else if(voice != 4){                          //length selection mode, turns all active beats white and others off
      for(int i = 0; i < 16; i++){
        if(i <= lengths[saveSlot]){
          strip.setPixelColor(i, 0, 0, 0, 15);
        }else{
          strip.setPixelColor(i, 0, 0, 0, 0);
        }
      }
    }
  }
  
  else if(patternChange){                         //if pattern has been changed recently, pattern nr is displayed with led
    for(int i = 0; i < 16; i++){
        if(i == pattern){
          strip.setPixelColor(i, 0, 0, 0, 15);
        }else{
          strip.setPixelColor(i, 0, 0, 0, 0);
        }
      }
  }
  
  else{                                           //normal operating mode, shows active beats in color of voice, turns others off
    if(voice != 4){                               //if in voice editing mode
      for(int i = 0; i < 16; i++){                
        if(beat[saveSlot][i]){                    //displays active beats for current voice
          strip.setPixelColor(i, voiceColor[voice][0], voiceColor[voice][1], voiceColor[voice][2], voiceColor[voice][3]);
        }
        else{
          strip.setPixelColor(i, 0, 0, 0, 0);
        }
      }
    }
    else{                                        //if in performance mode, all voices are displayed with voice 1 as highest priority
      for(int i = 0; i < 16; i++){
        strip.setPixelColor(i, 0, 0, 0, 0);
      }
      for(int j = 0; j < 4; j++){
        for(int i = 0; i < 16; i++){                  
          if(beat[3 - j + 4 * pattern][i]){
            strip.setPixelColor(i, voiceColor[3 - j][0], voiceColor[3 - j][1], voiceColor[3 - j][2], voiceColor[3 - j][3]);
          }
        }
      }
    }
    
    for(int i = 0; i < 16; i++){                  //highlights current beat
      if(currentBeat[voice] == i){
        strip.setPixelColor(i, 0, 0, 0, 25);
      }
    }
  }
  strip.show();
}


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Generates random beat --------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------

void randomBeat(){                                //creates a random beat for all 4 voices with voice 1 the most beats and voice 4 the fewest
  for(int i = 0; i < 4; i++){
    for(int j = 0; j < 16; j++){
      if(random(10) > 4 + i){
        beat[16 + i][j] = true;
      }else{
        beat[16 + i][j] = false;
      }
    }
  }
}


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Updates the beat -------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------

void advanceBeat(){                               //updates currentBeat, if it reached end for a voice it rolls over to first beat
  if(pattern != 4){
    for(int i = 0; i < 5; i++){
      if(i < 4){
        if(currentBeat[i] < lengths[i + 4 * pattern]){
          currentBeat[i]++;
        }else{
          currentBeat[i] = 0;
        }
      }else{
        if(currentBeat[i] < lengths[0 + 4 * pattern]){
          currentBeat[i]++;
        }else{
          currentBeat[i] = 0;
        }
      }
    }
  }
  else{                                           //if on random pattern, new pattern is generated when beat rolls over to first position
    if(currentBeat[0] < 15){
      currentBeat[0]++;
    }else{
      currentBeat[0] = 0;
      randomBeat();
    }
    for(int i = 0; i < 4; i++){
      currentBeat[i + 1] = currentBeat[0];
    }
  }
}


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Updates the outputs ----------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------

void output(){
  if(!trig && previousBeat + gateDuration > currentTime){           //if in gate mode and during active gate time
    for(int i = 0; i < 4; i++){
      digitalWrite(outputPins[i],beat[saveSlot][currentBeat[i]]);
    }
  }
  else if(trig & previousBeat + trigDuration > currentTime){        //if in trig mode and during acrive trig time
    for(int i = 0; i < 4; i++){
      digitalWrite(outputPins[i],beat[saveSlot][currentBeat[i]]);
    }
  }
  else{                                                             //if not in active time, outputs are low
    for(int i = 0; i < 4; i++){
      digitalWrite(outputPins[i],LOW);
    }
  }
}


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Checks time ------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------

void checkTiming(){                                                 //checks if enough time has passed to advance to the next beat
  
  currentTime = millis();                                           //gets current time in ms
  
  if(currentTime - previousBeat >= beatDuration){                   //if it is time for new beat
    previousBeat = currentTime;
    if(!sync){
      advanceBeat();
    }
  }
  
  if(patternChange && currentTime - previousPatternTime >= patternChangeDuration){ //if enough time has passed since pattern pot was last moved, returning to normal LED display
    patternChange = false;
    
    if(gateChange){                                                 //if gate length has changed, causes EEPROM write
      stateChanged = true;
      gateChange = false;
    }
  }
}


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Checks Sync Input ------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------

void checkSync(){                                                   //advances the beat on every rising flank of the sync input
  if(analogRead(syncPin) > 600){
    if(!syncHigh){
      syncHigh = true;
      advanceBeat();
    }
  }
  else{
    syncHigh = false;
  }
}


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Writes to EEPROM -------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------

//Writes current beat patterns to EEPROM
//beat patterns are converted to 2byte per voice
//order is 1byte gate length, then repeated for each voice of each pattern 2byte pattern, 1byte length
//as writes are rated to 100'000, location is moved by increment of 64byte every time to use EEPROM uniformally
//to find newest entry, first byte is incremented (rolls over at 255), so looking for highest number will get newest

void writeEEPROM(){
  stateChanged = false;
  
  int value = 0;                                                    //overcomplicated way of finding the next save location
  int previousValue = 0;
  int startAddress = 64;
  
  for(int i = 0; i < 16; i++){                                      //finds newest starting address by comparing first byte of all locations
    value = EEPROM.read(i*64);
    if(value == previousValue + 1 || previousValue == 255 || (i == 0 && value != 0)){
      previousValue = value;
      if(i < 15){
        startAddress = (i + 1) * 64;
      }else{
        startAddress = 0;
      }
    }
  }

  int index = 0;
  if(previousValue != 255){
    index = previousValue + 1;
  }  
  
  EEPROM.update(startAddress,index);                                //writes counter to first byte
  EEPROM.update(startAddress+1,gateLength);                         //writes gateLength to second byte
  
  for(int pat = 0; pat < 4; pat++){                                 //the 2 nested for loops iterate through the patterns and voices
    for(int voi = 0; voi < 4; voi++){
      int beat1 = 0;                                                //the 16 bool values for the beats get converted to 2 integers
      for(int i = 0; i < 8; i++){
        if(beat[voi + 4 * pat][i]){
          beat1 = beat1 + (1 << i);
        }
      }
      int beat2 = 0;
      for(int i = 0; i < 8; i++){
        if(beat[voi + 4 * pat][8+i]){
          beat2 = beat2 + (1 << i);
        }
      }

      int voiceStart = startAddress + 2 + 3 * (voi + 4 * pat);      //writing the two integers of the beats and the length to EEPROM
      EEPROM.update(voiceStart, beat1);
      EEPROM.update(voiceStart + 1, beat2);
      EEPROM.update(voiceStart + 2, lengths[voi + 4 * pat]);
    }
  }
}


// ---------------------------------------------------------------------------------------------------------------------------
// -------------------------------------------- Main Loop --------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------------

void loop() {
  readPots();
  readSwitches();
  if(stateChanged){
    writeEEPROM();
  }
  displayLEDs();
  checkTiming();
  if(sync){
    checkSync();
  }
  output();
}

type or paste code here

What makes you think the code is the problem? Have you changed the code from the YouTube video? Did you change the circuit in any way? Please post the schematic.

I'm not assuming anything at this point, just trying to check things off.

No changes to the code or circuit. I used the provided files and had them made up by PCBway.

I thought it was an issue with the build, so I rebuilt the unit with a fresh PCB, but still same problem. The only part I haven't rebuilt is the small PCB's with the neopixels, but they all seem to work.

The pots I used are 10k, but nowhere does it mention their rating in the build or in the BOM. So I just went with the most commonly used rating wrt arduinos. Maybe an issue?

Hi, @gaar_baard
Welcome to the forum.

Can you post Exported jpg images of your PCB please?

What is the input voltage Vin to your PCB?
What value is Vcc?
What do the switches do?
What do each of the pots do?

Thanks.. Tom.... :smiley: :+1: :coffee: :australia:

Thanks for the welcome.


Sorry the conversion is a little janky for the PCB images.

Here's the Github, GitHub - datulab/diyArduinoSequencer: Files to the project I showcased on YouTube.

Input is 12V and has a 5V converter via an LM7805, which powers the LED's separately from the nano, the nano is just providing the Data line which has a 330ohm resistor inline.

VCC is 5V.

The sequencer is a 16 step X 4 Voices, with one additional random setting which is "Voice 5". Each momentary switch either turns the step on or off to create a pattern.

The top switches on the right are for, Pattern Length, Trigger or Gate mode and Sync internal clock or external clock mode.

The three pots are, Voice Selection, BPM and Pattern Adjust.

Hi, @gaar_baard
Thanks for the info.

Have you got a DMM to measure the 5V supply to see if it is stable?
What are the values of C1 and C2 at the regulator?

Have you written some test code to check your pots and switches?
Can you measure the voltages at A0, A1 and A3 to see if they sweep from 0V to 5V smoothly?

Thanks.. Tom.... :smiley: :+1: :coffee: :australia:

The voltage is stable at 5,01- 5,02V and the caps are 10uf 63V.

The pots sweep smoothly from 0V to 5V and change the colours at around each 1V increment. But I have no idea if the 10K pots are correct as their rating was never mentioned in the build or BOM.

I've tried a test code for the pots, they work, light up LED's 1-16 sequentially and a test code for the switch matrix, also worked, lit up each corresponding LED, no problem.

Still same issue with the original code persists.

Thanks for the assist.

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