Euclidean Rhythm Generator [Arduino Based]

I found an Arduino based Euclidean Rhythm Generator today and figured I would have a go a building it soon.


It was posted on Github by katspaugh

I made a Stripboard Layout for it with 2 different LED boards that are interchangeable.

Main Board -

LED Boards -

Boards attached -
LED Configuration 1


LED Configuration 2

I will let everyone know how it goes… lol

(Code - )

#include <EEPROM.h>

#define BUTTON_PIN 11
#define CLOCK_PIN 12
#define OUT_PIN 13
#define INVERTED_OUT_PIN 10
#define RESET_PIN A7
#define LONG_PRESS 600
#define SHORT_PRESS 10
#define MAX_STEPS 8

int leds[] = { 5, 4, 3, 2, 7, 9, 8, 6 };

bool alt_positions = false;
int active_steps1 = 1;
int active_steps2 = 1;
int offset = 0;
int counter = -1;
int steps = MAX_STEPS;
bool positions1[MAX_STEPS] = {};
bool positions2[MAX_STEPS] = {};

bool send_tick = false;
bool clock_state = false;
bool last_clock_state = false;
bool last_button_state = false;
bool last_reset_state = false;
int button_pressed_time = 0;
int long_pressed_time = 0;

bool getPosition(uint8_t index, bool positions[MAX_STEPS]) {
int step_idx = (index + offset) % steps;
return positions[step_idx];
}

void checkButton() {
bool button_state = digitalRead(BUTTON_PIN) == LOW;
if (!button_state && last_button_state == button_state) return;
int now = millis();

// Save the time when the button was pressed
if (button_state && !last_button_state) {
button_pressed_time = now;
long_pressed_time = now;
}
last_button_state = button_state;

int press_time = now - button_pressed_time;

// On long press
if (button_state && press_time >= LONG_PRESS) {
if (now - long_pressed_time >= LONG_PRESS) {
//offset += 1;
//if (offset >= steps) offset = 0;

  alt_positions = !alt_positions;

  setActiveLeds();
  long_pressed_time = now;    
}
return;

}

// On button up after a short press
if (!button_state && press_time >= SHORT_PRESS && press_time < LONG_PRESS) {
if (alt_positions) {
active_steps2 += 1;
if (active_steps2 > steps) active_steps2 = 1;
setPositions(active_steps2, positions2);
EEPROM.write(1, active_steps2);
} else {
active_steps1 += 1;
if (active_steps1 > steps) active_steps1 = 1;
setPositions(active_steps1, positions1);
EEPROM.write(0, active_steps1);
}
setActiveLeds();
return;
}
}

void checkReset() {
bool reset = analogRead(RESET_PIN) > 500;
if (reset == last_reset_state) return;
last_reset_state = reset;
if (reset) counter = 0;
}

void setPositions(int active_steps, bool positions[MAX_STEPS]) {
for (int i = 0; i < steps; i++) {
positions[i] = false;
}

if (active_steps == 1 || steps - active_steps <= 1) {
for (int i = 0; i < active_steps; i++) {
positions[i] = true;
}
return;
}

int remainder = active_steps;
int quotient = 0;
int skip = 0;

while (remainder > 0) {
quotient = floor(steps / remainder);
int rem = steps % remainder;
if (rem) quotient += 1;

for (int i = 0; i < steps; i++) {
  if ((i + 1) % quotient == 0) {
    positions[(i + skip) % steps] = true;
  }
}

skip += 1;
remainder -= floor(steps / quotient);

}
}

void setLed(int index, bool active) {
int pin = leds[index];
digitalWrite(pin, active ? HIGH : LOW);
}

void setActiveLeds() {
for (int i = 0; i < steps; i++) {
setLed(i, getPosition(i, alt_positions ? positions2 : positions1));
}
}

void onClockOn() {
counter += 1;
if (counter >= steps) counter = 0;

bool is_active1 = getPosition(counter, positions1);
digitalWrite(OUT_PIN, is_active1);

bool is_active2 = getPosition(counter, positions2);
digitalWrite(INVERTED_OUT_PIN, is_active2);

setLed(counter, alt_positions ? !is_active2 : !is_active1);
}

void onClockOff() {
digitalWrite(OUT_PIN, LOW);
digitalWrite(INVERTED_OUT_PIN, LOW);
setActiveLeds();
}

void pciSetup(byte pin) {
*digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin)); // enable pin
PCIFR |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
PCICR |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}

ISR(PCINT0_vect) {
send_tick = true;
}

void setup() {
for (int i = 0; i < steps; i++) {
int pin = leds[i];
pinMode(pin, OUTPUT);
}

pinMode(CLOCK_PIN, INPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(OUT_PIN, OUTPUT);
pinMode(INVERTED_OUT_PIN, OUTPUT);

pciSetup(CLOCK_PIN);

active_steps1 = EEPROM.read(0);
active_steps2 = EEPROM.read(1);

setPositions(active_steps1, positions1);
setPositions(active_steps2, positions2);
setActiveLeds();
}

void loop() {
checkButton();
checkReset();

if (!send_tick) return;
send_tick = false;

clock_state = digitalRead(CLOCK_PIN);
if (clock_state == last_clock_state) return;
last_clock_state = clock_state;

clock_state ? onClockOn() : onClockOff();
}

6 Likes

From the README:

There’s no diode protection on any of the jacks, so only positive voltages please.

Or, y’know, put diode protection on the jacks.

4 Likes

Nit: The Nano power is wrong – Vin goes to a +5 V regulator, so you need a bit more than 5 V if you connect it to that pin (regulator drop out is ~1.2 V, Nano spec recommends 7-12 V). If you have regulated 5 V, connect it to the +5 V pin instead (or power via USB).

Nano schematics:

(LM1117 data sheet is here)

It would be connected to 12v.
Isn’t the VIN the 12v power input?

The AVR has protection diodes on all inputs, and with 10k series resistors they’ll most likely survive ±10 V. But if you don’t trust yourself :slight_smile:, adding external diodes won’t hurt.

3 Likes

Schematics say “5V / can also be 12V”. Recommended range according to the Nano spec is 7-12 V, so +12 V is ok but 5 isn’t.

(the Nano FAQ says 6-20 V, the ATmega328 can run at 4.5 V but you won’t get 5 V out, and 20 V is absolute max for the regulator, but then it has to burn away a 15 V drop so you risk frying it if you add any non-trivial load.)

EDIT: To clarify, the ATmega328 can run at voltages below 4.5 V but not at 16 MHz according to the specs, which is why this kind of works until it doesn’t :upside_down_face:

4 Likes

So in the original drawing he has up on Github, he obviously made a mistake by not connecting the resistor for input 2, but, my question is, does input 1 and input 2 actually jumper together?

1

No, if you look in the description in the README the two inputs are entirely different. Obviously the schematic’s messed up but it looks like the first input goes to D12 and the second to A7, and that appears to be confirmed by the code.

1 Like

Could you maybe edit the post and put the code in a preformatted text block so that the indentation in the code is preserved? It is difficult (and a bit annoying) to read the code like this.

1 Like

Here’s a link to the code in more readable form:

I have absolutely no clue how - It is why I put the Link to the Github. lol

Thanks!!! for that!

I do not understand arduino code in the slightest… like… at all… lol

You can paste code into discourse comments, but you have to either add ``` (three tick marks) before and after the block, or add four spaces before each line:

// some code with no indentation
void function(int something) {
return something;
}

// 4 spaces before each line
void function(int something) {
     return something;
}
// ``` before and after the code block
void function(int something) {
    return something;
}

(Note that last one does syntax highlighting. If it fails to figure out the right language, you can spell it out after the initial ticks, see here).

Large code blocks aren’t very practical for the reader, though, so best used for short code fragments. When the code is on github, you can just link to the file, and discourse will figure out the rest by itself.

3 Likes

Update - Added the IN/OUT’s and Switch to the layout.

If someone builds this - please Note that there is an additional Resistor and Trace cut on the board -
(See Highlighted sections)

3 Likes

I made my own version on a Euclidean Generator with an LED ring. So far it’s only a proof of concept and I have no idea if it still works with more rings added (I am very inexperienced with timers and interrupts) and the encoder is sometimes a bit sluggish, but I thought you might still enjoy it :slight_smile:

6 Likes

Friggin nice!!
Is this based on the one I posted above?

I am super happy to see it working haha

1 Like

No, I peeked at this code here: https://www.pjrc.com/euclidean-rhythms-sequencer/
But mine is a lot simpler!

1 Like