A few questions about a programmable step sequencer

Hello Arduino forum,

I've had the Uno development board since December and I've been doing some prototyping with it.

Now, I feel like I should make something useful out of it.. I've narrowed down my choices to a few projects and have some questions regarding programming (and the hardware too)

First of all I have in mind this nifty "bassline" program that uses potentiometers (or faders) to cycle through pins to read values and print them as tone (it's 4 step as the I/O and the breadboard space is very limited)

void setup() {
  Serial.begin(9600);
}

void loop() {
  int tempo = 500 ;
  int noteLength = 200 ;
  int step1 = analogRead(A0) / 4 ;
  int step2 = analogRead(A1) / 4 ;
  int step3 = analogRead(A2) / 4 ;
  int step4 = 100 ;
  Serial.println(step1) ;
  Serial.println(step2) ;
  Serial.println(step3) ;

   tone(8, step1, noteLength);
    delay(tempo);
   tone(8, step2, noteLength);
    delay(tempo);
   tone(8, step3, noteLength);
    delay(tempo);
   tone(8, step4, noteLength);
    delay(tempo);
}

It could be further extended with tempo, play/pause, note length, filters etc whatnot. (edit : note that I only had 3 pots to work with..I also simplified the code by removing digitalWrite bits for LEDs) The problem here is the I/O because the Uno has 6 analog ins and for 8 steps you'd need 8 or more. I've read about GPIO shields which could come handy here.

Other than that it's very funky, analogue-y in the sense that you'll almost never get the notes in tune. (edit : tbh it's just downright atonal but I liked it for some reason)

Here's another sketch :

void setup() {
  Serial.begin(9600) ;
}

void loop() {
  int tempo = 1000 ;
  int noteLength = 100 ;
  int notes[5] = {262,294,330,349,0} ;
  int seq[4] ;
  int keyVal = analogRead(A0) ;
  int stepVal = analogRead(A1) ;
  Serial.println(keyVal) ;
  Serial.println(stepVal) ;
  
if (stepVal == 1023 && keyVal == 1023){
  seq[0] = notes[0] ;
  }
  else if (stepVal >= 990 && stepVal <=1010 && keyVal >= 990 && keyVal <= 1010){
  seq[0] = notes[1] ;
  }
  else if (stepVal >= 505 && stepVal <= 515 && keyVal >= 505 && keyVal <= 515){
  seq[0] = notes[2] ;
  }
  else if (stepVal >= 5 && stepVal <=10 && keyVal >= 5 && keyVal <= 10){
  seq[0] = notes[3] ;
  }
  else if (stepVal == 1023){
  seq[0] = notes[5] ;
  }
if (stepVal == 1023 && keyVal == 1023){
  seq[1] = notes[0] ;
  }
  else if (stepVal >= 990 && stepVal <=1010 && keyVal >= 990 && keyVal <= 1010){
  seq[1] = notes[1] ;
  }
  else if (stepVal >= 505 && stepVal <= 515 && keyVal >= 505 && keyVal <= 515){
  seq[1] = notes[2] ;
  }
  else if (stepVal >= 5 && stepVal <=10 && keyVal >= 5 && keyVal <= 10){
  seq[1] = notes[3] ;
  }
  else if (stepVal == 1023){
  seq[1] = notes[5] ;
  }   
if (stepVal == 1023 && keyVal == 1023){
  seq[2] = notes[0] ;
  }
  else if (stepVal >= 990 && stepVal <=1010 && keyVal >= 990 && keyVal <= 1010){
  seq[2] = notes[1] ;
  }
  else if (stepVal >= 505 && stepVal <= 515 && keyVal >= 505 && keyVal <= 515){
  seq[2] = notes[2] ;
  }
  else if (stepVal >= 5 && stepVal <=10 && keyVal >= 5 && keyVal <= 10){
  seq[2] = notes[3] ;
  }
  else if (stepVal == 1023){
  seq[2] = notes[5] ;
}
if (stepVal == 1023 && keyVal == 1023){
  seq[3] = notes[0] ;
  }
  else if (stepVal >= 990 && stepVal <=1010 && keyVal >= 990 && keyVal <= 1010){
  seq[3] = notes[1] ;
  }
  else if (stepVal >= 505 && stepVal <= 515 && keyVal >= 505 && keyVal <= 515){
  seq[3] = notes[2] ;
  }
  else if (stepVal >= 5 && stepVal <=10 && keyVal >= 5 && keyVal <= 10){
  seq[3] = notes[3] ;
  }
  else if (stepVal == 1023){
  seq[3] = notes[5] ;
  }
else{
  tone(8, seq[0], noteLength) ;
    delay(tempo) ;
  tone(8, seq[1], noteLength) ;
    delay(tempo) ;
  tone(8, seq[2], noteLength) ;
    delay(tempo) ;
  tone(8, seq[3], noteLength) ;
    delay(tempo) ; 
  }
}

This one is a bit different animal. Basically it uses two 220ohm/10kOhm/1MOhm resistor ladders with buttons; one to create the incoming voltage (=note) and fetch the correct frequency from the "notes" array and the other to select a pattern step. The note is then stored to the "seq" array when they're both pressed simultaneously but for me it didn't work.. (edit : I also tested it with the steps using digital pins, the problem is the I/O because one would need 8+8 for LEDs and steps so there'd be no space for play/pause and output)

I've been thinking whether the "seq" array should be under the "setup" program should the loop overwrite it, one solution could be to use EEPROM.write and address the values there. I assume the data byte is written in the SRAM so I'd have to divide the incoming values by 4 (because 255 is the maximum value for 8-bit) which means I'd have to either rewrite the "if" parts (there could also be a problem with the voltage values as I'd be narrowing the resolution) or use another array/lookup-table so I'd be writing only note values (1, 2, n..)

Another idea was a drum machine which basically works in the same manner but instead I'd define separate arrays for different sounds (kick, snare etc) and use 4th's, 8th's and 16th's for the sequence.

Anyway, what's your take on this?

Thanks in advance,

-ef

Too much thinking and too little doing. Test your ideas, rather than writing them to the forum.

Paul

Get one or two 8-channel ADCs, have as many inputs as you like, with 12-bit resolution.
Example:
https://www.digikey.com/product-detail/en/microchip-technology/MCP3208-CI-P/MCP3208-CI-P-ND/305928

A 74HC4051 or 74HC4067 type analog multiplexer can also be used to extend the number of analog inputs.

Paul_KD7HB:
Too much thinking and too little doing. Test your ideas, rather than writing them to the forum.

Paul

Good idea. Thanks for the motivation!

CrossRoads:
Get one or two 8-channel ADCs, have as many inputs as you like, with 12-bit resolution.
Example:
MCP3208-CI/P Microchip Technology | Integrated Circuits (ICs) | DigiKey

I didn't know this was possible but I'm giving it a thought.

sterretje:
A 74HC4051 or 74HC4067 type analog multiplexer can also be used to extend the number of analog inputs.

I've read about extending the I/O with 8-bit shift registers such as the 74HC595 and GPIO shields but I'm a little confused as to how one would go on about addressing the pins in the code, like is there a library of some sort etc or does it function using sign bits and whether the multiplexing degrades/slows down the overall system performance?

Anyway, I've been looking at some candidates for casing as well as other components for the final product.. then I started thinking about the PSU and it began to concern me because the lowest values are currently in the range of 5 to 10 which is roughly 25-50mV and ripple voltages could cause significant errors to the inputs.. any thoughts on that or am I just microscoping my plan?

EDIT : another thing was MIDI connectivity which I also gave a thought as it would save the trouble of riding the tempo

There are examples on the web; I can't vow for the correctness, but it will be a start. You are interested in the multiplexing.

Analog Multiplexer/Demultiplexer - 4051
Tutorial – 74HC4067 16-Channel Analog Multiplexer Demultiplexer

Regarding PSU, not my area of experience but these are my thoughts

Your ADC already has a 1 bit fluctuation (approx. 5mV); you can throw that last bit away. As you're dividing your analog reads by 4, you throw the last two bits away (so you have 20mV accuracy).
2)
If you power both the Arduino and the potentiometers from the same 5V, fluctuations in PSU output voltage will affect both sides and therefore will not affect the reading. During development you can use the USB power and the 5V pin as output for your potentiometers. You should in theory be able to power the Arduino on the 5V pin and also power the potentiometers from it (just split the power supply line into two).

Note
It might not be a good idea to power on USB and on the 5V pin at the same time.

sterretje:
There are examples on the web; I can't vow for the correctness, but it will be a start. You are interested in the multiplexing.

I've read a few sequencer tutorials but they were either too complex or not what I was looking for. Yes, it's in my interest to get the project working, but there are various methods; the resistor ladder is one of them. It's partially copied from the projects book btw.

Regarding PSU, not my area of experience but these are my thoughts

Your ADC already has a 1 bit fluctuation (approx. 5mV); you can throw that last bit away. As you're dividing your analog reads by 4, you throw the last two bits away (so you have 20mV accuracy).

I'm not following here.. but I'm aware that excessive dividing in code should be avoided.

If you power both the Arduino and the potentiometers from the same 5V, fluctuations in PSU output voltage will affect both sides and therefore will not affect the reading. During development you can use the USB power and the 5V pin as output for your potentiometers. You should in theory be able to power the Arduino on the 5V pin and also power the potentiometers from it (just split the power supply line into two).

This makes sense.. but wouldn't there be a slight delay (=slew rate) between the two?

Anyway, regarding stuff like this; I've worked in an aquacultural farm/research facility during my studies as well as a supplier for dairy/cattle industry and they use shuttle feeders etc, in other words robots which I believe core functionality-wise utilise more or less the same sort of programming. I've even reprogrammed one (and installed a WIFi card in another) because it had a broken IR transmitter/receiver for the data input; the station-specific values came from a PC console. Before that we had to do feeding the old fashioned way because the robots had no manual override.. I even have a diary from those exciting times.

Sorry for posting twice in a row but I just noticed an amateur mistake in the first sketch where the divided analogReads are defined as int's while the results could be float's. I don't know how the language deals with cases like this but it compiled so it's all good I guess.

Meanwhile, I've been scheming the hardware and connections on paper to get an idea of the BOM and the I/O needs.

EDIT : as for the (partial) BOM;

-case (pref. angled, for a retro C64 look)
-12VDC PSU (or 9VDC , I was thinking a SMPS, like a Mean Well PD)
-8x 30-60mm 10k lin (or log) faders for notes (or step switches with resistors/trimmers)
-8x fader caps
-8x push buttons/switches (for steps, in series with the faders)
-1x push button/switch (start)
-2x 10k lin (or log) pots (tempo&length)
-8x LEDs (for sequence)
-1-10k log pot (for filter)
-100uF/25V capacitor
-6,3mm jack (for output)
-10k log pot for volume
-on/off power switch
-power connector
-power-on LED
-knobs for pots
-rubber feet
-handles
-wire
-stripboard
-misc stuff like wire holders, PCB mounts, printable decal for the front panel etc
-GPIO shield

This would require (using the first sketch) 10 analog inputs, 1 digital input (or an analog) and 9 digital outputs.. there's surely something I've missed but that should cover it. I was thinking whether I should get a Mega instead? Also, what about putting the push buttons pre- or post fader (to avoid clicks)?

EDIT 2: forgot the MIDI port.. although it has USB. Also another set of faders to an input selector was one idea, sort of like a simple memory bank to store a sequence. With 2 bars that's 2x8 faders and an 8-pole input selector switch (or two 4-poles). I made some calculations and I think a very economy-conscious (and industrious) individual could under certain circumstances build one realistically for about 100-200€.

EDIT 3: I also have an LCD that came with the starter kit.. it could be used to denote tempo and the sequence etc. The problem is the I/O, really (and it adds up to the overall cost of the unit; however a prototype, it'd be a sort of a "flagship" model, I could make a "Robinson Crusoe"-version later on) To pass the time I made a prototype RC filter but it didn't work for some reason.

EDIT 4: also tried using a pow() function on the input values..

Tested with constrain() set to 33-262.. it may be too low I think, I also set a passive LPF in the output with a 10k pot and 100uF cap so it's borderline atavistic but nevertheless a bit more pleasing to the ear. Might test later with different values.

EDIT : to further clarify; the 2nd sketch yielded a rather inconsistant stream of 0's and 1's and slowed down the board. Or should I use PROGMEM instead?

EDIT 2 : and yes; I'm trying to build a TB-303 clone.. well, not a clone but something to fulfill my acid needs.

I've been testing a slide function.. that's one more analog input. Also tested with headphones, it goes low but sounds kinda kitschy. I think the filter sort of saves it.

EDIT : here's a simple CAD drawing.. it's missing the slide and the grounds for the input voltages though but you get the idea.

EDIT 2 : fixed the missing pot ground in the filter (the pot could be linear too I think, and much smaller value, like 1k) I've also worked on the code a bit but nothing too dramatic.

EDIT 3 : here's the code as of now.. there's an error in the code that it constrains the step value to 255 but you could substitute it with 262 (or 247)

void setup() {
  Serial.begin(9600) ;
  pinMode(A0, INPUT) ;
  pinMode(A1, INPUT) ;
  pinMode(8, OUTPUT) ;
  pinMode(13, OUTPUT) ;
}

void loop() {
  int tempo = 500 ;
  int noteLength = 300 ;
  int slide = 10 ;
  int slideTime = 100 ;
  int step1 = analogRead(A0) / 4 ;
  int step2 = analogRead(A1) / 4 ;
  int step3 = 33 ; 
  int step4 = 33 ;
    step1 = constrain(step1, 33, 255) ;
    step2 = constrain(step2, 33, 255) ;
    step3 = constrain(step3, 33, 255) ;
    step4 = constrain(step4, 33, 255) ;
  Serial.println("A0:") ;
    Serial.println(analogRead(A0)) ;
  Serial.println("Step1:") ;
    Serial.println(step1) ;
  Serial.println("A1:") ;
    Serial.println(analogRead(A1)) ;

  digitalWrite(13, HIGH) ;
  tone(8, step1 + slide, slideTime / 2) ;
  tone(8, step1 + slide / 2, slideTime / 2) ;
   tone(8, step1, noteLength - slideTime) ;
    delay(tempo) ;
      digitalWrite(13, LOW) ;
  
  tone(8, step2 + slide, slideTime) ;
   tone(8, step2, noteLength - slideTime) ;
    delay(tempo) ;
      
  tone(8, step3 + slide, slideTime) ;
   tone(8, step3, noteLength - slideTime) ;
    delay(tempo) ;
   
  tone(8, step4 + slide, slideTime) ;
   tone(8, step4, noteLength - slideTime) ;
    delay(tempo) ;
}

Here's a 16bit/44,1kHz sound sample/demo... recorded through an Allen & Heath Zed10FX and normalized in Audacity

EDIT : oh yes, I used a 9V wall wart that's from a NI Audio soundcard if I remember correctly

EDIT 2 : here's a pic of the protoboard

EDIT 3 : changed the link to Dropbox.. I also started working on an EDM track with the sample.

Working on a serial port start/stop, possibly even sync.

Yet another idea was an eurorack slot and CV/Gate out.. I don't have any eurorack modules though.

EDIT : or an overdrive/compressor pedal in the output to serve as a preamp and make it sound more brutal

Just tested a Powershell script to control the device.. no luck.

EDIT : originally the intent was to make a program to control tempo and notes in VB.. I might try it later though.

EDIT 2 : one option would be to use a touch screen for control.. I have no prior experience programming one though. Another problem is that they're unreadable in certain lighting conditions and most likely prone to become unresponsive when exposed to dirt, sweat etc

EDIT 3 : been checking the resistor value for the LPF... my multimeter didn't show any readings for some strange reason.

I think for stability reasons it'd be better to go for 12/24-pos rotary/step switches with resistors/trimmers to dial the notes instead of potentiometers.. I've been busy with another project.

EDIT : here's the revised schematic..