@@ -52,6 +52,10 @@ int now = 0;
5252bool lastButtonState = HIGH ;
5353int lengthA = 8 ;
5454int lengthB = 8 ;
55+ int directionA = 0 ;
56+ int directionB = 0 ;
57+ int directionStateA = 1 ;
58+ int directionStateB = 1 ;
5559
5660// --- POT movement tracking (mode-aware) ----------------
5761int potLastStablePrimary[3 ]; // baseline used when button NOT pressed
@@ -64,6 +68,11 @@ int seqKnobLastStableSecondary[2];
6468int seqKnobLastRaw[2 ];
6569unsigned long seqKnobLastCheck[2 ];
6670
71+ int seqKnob2LastStablePrimary[2 ];
72+ int seqKnob2LastStableSecondary[2 ];
73+ int seqKnob2LastRaw[2 ];
74+ unsigned long seqKnob2LastCheck[2 ];
75+
6776
6877const unsigned long POT_CHECK_INTERVAL = 2000UL ; // µs between physical reads (~2 ms)
6978const int POT_NOISE_THRESHOLD = 4 ; // ignore tiny ±4 jitter
@@ -111,6 +120,19 @@ void setup() {
111120 seqKnobLastRaw[1 ] = vB;
112121 seqKnobLastCheck[1 ] = micros ();
113122
123+ int vA2 = readMux (MUX_SIG0 , 1 );
124+ seqKnob2LastStablePrimary[0 ] = vA2;
125+ seqKnob2LastStableSecondary[0 ] = vA2;
126+ seqKnob2LastRaw[0 ] = vA2;
127+ seqKnob2LastCheck[0 ] = micros ();
128+ delayMicroseconds (60 );
129+
130+ int vB2 = readMux (MUX_SIG1 , 1 );
131+ seqKnob2LastStablePrimary[1 ] = vB2;
132+ seqKnob2LastStableSecondary[1 ] = vB2;
133+ seqKnob2LastRaw[1 ] = vB2;
134+ seqKnob2LastCheck[1 ] = micros ();
135+
114136 // Initialize DAC (MCP4728)
115137 if (!dac.begin ()) {
116138 while (1 ); // DAC initialization failed
@@ -146,6 +168,20 @@ void syncPotBaselinesOnModeChange(bool toSecondary) {
146168 seqKnobLastStablePrimary[1 ] = vB;
147169 seqKnobLastRaw[1 ] = vB;
148170 }
171+
172+ int vA2 = readMux (MUX_SIG0 , 1 );
173+ int vB2 = readMux (MUX_SIG1 , 1 );
174+ if (toSecondary) {
175+ seqKnob2LastStableSecondary[0 ] = vA2;
176+ seqKnob2LastRaw[0 ] = vA2;
177+ seqKnob2LastStableSecondary[1 ] = vB2;
178+ seqKnob2LastRaw[1 ] = vB2;
179+ } else {
180+ seqKnob2LastStablePrimary[0 ] = vA2;
181+ seqKnob2LastRaw[0 ] = vA2;
182+ seqKnob2LastStablePrimary[1 ] = vB2;
183+ seqKnob2LastRaw[1 ] = vB2;
184+ }
149185}
150186
151187void loop () {
@@ -177,9 +213,11 @@ void loop() {
177213 bool p2_moved = hasPotMoved (2 );
178214 bool seqA_moved = hasSeqKnobMoved (0 );
179215 bool seqB_moved = hasSeqKnobMoved (1 );
216+ bool seqA2_moved = hasSeqKnob2Moved (0 );
217+ bool seqB2_moved = hasSeqKnob2Moved (1 );
180218
181219 // Update controls
182- readControls (p0_moved, p1_moved, p2_moved, seqA_moved, seqB_moved);
220+ readControls (p0_moved, p1_moved, p2_moved, seqA_moved, seqB_moved, seqA2_moved, seqB2_moved );
183221
184222 if (digitalRead (BUTTON_PIN ) == LOW ) {
185223 if (p0_moved) {
@@ -192,6 +230,10 @@ void loop() {
192230 updateScalingVisualization (map (lengthA, 1 , 8 , 0 , 1023 ), 200 , 20 , 0 );
193231 } else if (seqB_moved) {
194232 updateScalingVisualization (map (lengthB, 1 , 8 , 0 , 1023 ), 0 , 200 , 200 );
233+ } else if (seqA2_moved) {
234+ updateScalingVisualization (map (directionA, 0 , 3 , 0 , 1023 ), 200 , 20 , 0 );
235+ } else if (seqB2_moved) {
236+ updateScalingVisualization (map (directionB, 0 , 3 , 0 , 1023 ), 0 , 200 , 200 );
195237 }
196238 }
197239
@@ -210,15 +252,15 @@ void handleClockPulse() {
210252 now = micros ();
211253 static int counterA = 0 , counterB = 0 ;
212254
213- // Advance sequence steps based on clock division
255+ // Advance sequence steps based on clock division and playback mode
214256 if (++counterA >= divisionA) {
215257 counterA = 0 ;
216- step1 = (step1 + 1 ) % lengthA ;
258+ step1 = getNextStep (step1, lengthA, directionA, directionStateA) ;
217259 }
218260
219261 if (++counterB >= divisionB) {
220262 counterB = 0 ;
221- step2 = (step2 + 1 ) % lengthB ;
263+ step2 = getNextStep (step2, lengthB, directionB, directionStateB) ;
222264 }
223265
224266 // Update LEDs with sequences
@@ -266,21 +308,41 @@ void updateLEDs() {
266308 strip.show ();
267309}
268310
269- void readControls (bool p0_moved, bool p1_moved, bool p2_moved, bool seqA_moved, bool seqB_moved) {
311+ void readControls (bool p0_moved, bool p1_moved, bool p2_moved, bool seqA_moved, bool seqB_moved, bool seqA2_moved, bool seqB2_moved ) {
270312 // Read next pots for sequences A and B
271- int nextStepA = (step1 + 1 ) % NUM_LEDS ;
272- int nextStepB = (step2 + 1 ) % NUM_LEDS ;
313+ int nextStepA = getNextStep (step1, lengthA, directionA, directionStateA) ;
314+ int nextStepB = getNextStep (step2, lengthB, directionB, directionStateB) ;
273315 if (digitalRead (BUTTON_PIN ) == LOW ) {
274316 if (seqA_moved) {
275317 int currentLengthKnobA = readMux (MUX_SIG0 , 0 );
276318 lengthA = map (currentLengthKnobA, 0 , 1023 , 1 , 8 );
277319 if (step1 >= lengthA) step1 = 0 ;
278320 }
279-
321+
280322 if (seqB_moved) {
281323 int currentLengthKnobB = readMux (MUX_SIG1 , 0 );
282324 lengthB = map (currentLengthKnobB, 0 , 1023 , 1 , 8 );
283- if (step2 >= lengthB) step2 = 0 ;
325+ if (step1 >= lengthB) step1 = 0 ;
326+ }
327+
328+ if (seqA2_moved) {
329+ int currentDirectionKnobA = readMux (MUX_SIG0 , 1 );
330+ directionA = currentDirectionKnobA / 256 ;
331+ if (directionA > 3 ) directionA = 3 ;
332+ if (directionA == 2 ) {
333+ directionStateA = 1 ;
334+ step1 = 0 ;
335+ }
336+ }
337+
338+ if (seqB2_moved) {
339+ int currentDirectionKnobB = readMux (MUX_SIG1 , 1 );
340+ directionB = currentDirectionKnobB / 256 ;
341+ if (directionB > 3 ) directionB = 3 ;
342+ if (directionB == 2 ) {
343+ directionStateB = 1 ;
344+ step2 = 0 ;
345+ }
284346 }
285347
286348 if (nextStepA != 0 ) {
@@ -354,6 +416,27 @@ void outputSequenceToDAC() {
354416 }
355417}
356418
419+ int getNextStep (int currentStep, int length, int direction, int &directionState) {
420+ if (direction == 0 ) {
421+ return (currentStep + 1 ) % length;
422+ } else if (direction == 1 ) {
423+ return (currentStep - 1 + length) % length;
424+ } else if (direction == 2 ) {
425+ int nextStep = currentStep + directionState;
426+ if (nextStep >= length - 1 ) {
427+ nextStep = length - 1 ;
428+ directionState = -1 ;
429+ } else if (nextStep <= 0 ) {
430+ nextStep = 0 ;
431+ directionState = 1 ;
432+ }
433+ return nextStep;
434+ } else if (direction == 3 ) {
435+ return random (0 , length);
436+ }
437+ return currentStep;
438+ }
439+
357440void updateScalingVisualization (int value, int r, int g, int b) {
358441 strip.clear ();
359442
@@ -507,6 +590,38 @@ bool hasSeqKnobMoved(int seqIndex) {
507590 return moved;
508591}
509592
593+ bool hasSeqKnob2Moved (int seqIndex) {
594+ bool secondaryMode = (digitalRead (BUTTON_PIN ) == LOW );
595+
596+ unsigned long nowTime = micros ();
597+ if (nowTime - seqKnob2LastCheck[seqIndex] < POT_CHECK_INTERVAL ) {
598+ return false ;
599+ }
600+
601+ int sigPin = (seqIndex == 0 ) ? MUX_SIG0 : MUX_SIG1 ;
602+ int currentValue = readMux (sigPin, 1 );
603+
604+ if (abs (currentValue - seqKnob2LastRaw[seqIndex]) <= POT_NOISE_THRESHOLD ) {
605+ currentValue = seqKnob2LastRaw[seqIndex];
606+ }
607+ seqKnob2LastRaw[seqIndex] = currentValue;
608+
609+ bool moved = false ;
610+ if (secondaryMode) {
611+ if (abs (currentValue - seqKnob2LastStableSecondary[seqIndex]) > POT_MOVE_THRESHOLD ) {
612+ seqKnob2LastStableSecondary[seqIndex] = currentValue;
613+ moved = true ;
614+ }
615+ } else {
616+ if (abs (currentValue - seqKnob2LastStablePrimary[seqIndex]) > POT_MOVE_THRESHOLD ) {
617+ seqKnob2LastStablePrimary[seqIndex] = currentValue;
618+ moved = true ;
619+ }
620+ }
621+
622+ seqKnob2LastCheck[seqIndex] = nowTime;
623+ return moved;
624+ }
510625
511626const int scales[8 ][36 ] = {
512627 // Chromatic scale
0 commit comments