Final project idea

From Fab Lab Wiki - by NMÍ Kvikan
Jump to: navigation, search
8.2.2016 (ish)

I decided on a sequencer and controllers for making analogue sound, for my final project. My current vision of it is a simple sequencer where I can easily manipulate the pitch, velocity and length of individual notes. You can also change the rate and number of notes in a loop. 8 to 16 notes for starters will be enough and I can extend the project later, once I have the basics right.

For manipulating the actual sound signal I'm visioning controllers that fit in your hand. A cube, pyramid or sphere could be turned to trigger tilt sensors and they would have pressure sensors under circular living hinge areas that could also be used to affect parameters like pitch, FM modulation, waveform etc. Touch sensors and LEDs could be put under surfaces to make the casing feel more transparent.

I'm looking for an intuitive music creation kit. That's essentially what would be inside. On the surface I'm planning to have natural materials to make the whole system a pleasure to touch.

The first thing I will do is learn how to program and build a very simple sequence of notes. Alternatively I could find an existing project like this and reduce from that.

I will be updating as I progress...




10.2.2016 UPDATE

Here are some iterations of code that I made, trying to figure out components of a simple step sequence of 4 LEDs. The first is copied from "BlinkWithoutDelay" from Arduino examples.

const int a = 13;

int ledState = LOW;

unsigned long prevMillis = 0;

const long freq = 1500;


void setup() {
 
 pinMode(1, OUTPUT);
 pinMode(2, OUTPUT);
 
}

void loop() {
 
 unsigned long currentMillis = millis();
 
 if (currentMillis - prevMillis >= freq) {
   prevMillis = currentMillis;
   
   if (ledState == LOW) {
     ledState = HIGH;
   }
   else {
     ledState = LOW;
   } 
 
 digitalWrite(a, ledState);
 
}
}

This is my first attempt to make 4 LEDs blink in sequence. The thought behind this code is having 1000 ms loop (looptime) and using division to light up the LEDs in sequence.

How this iteration is actually checking it: 1000/4 = LED a (1000/4)*2 = LED b (1000/4)*3 = LED c 1000 = LED d

The problem with the division code in this version is that I'm using = instead of ==. The former is an assigment operator, which means it sets the value instead of checking it. Also the digitalWrite function is not set right. I will be fixing these in the next versions.

It compiles with no problems, but does nothing to the LEDs.

int a = 13;
int b = 12;
int c = 11;
int d = 10;

const int looptime = 1000;

int zero = 0;

void setup() {
 
 pinMode(a, OUTPUT);
 pinMode(b, OUTPUT);
 pinMode(c, OUTPUT);
 pinMode(d, OUTPUT);
 
}

void loop() {

 int time = millis();
 
 if (time = looptime/4) 
  digitalWrite(
  a = HIGH;
  b = LOW;
  c = LOW;
  d = LOW;
  );
 
 if (time = (looptime/4) * 2)
  digitalWrite( 
  a = LOW;
  b = HIGH;
  c = LOW;
  d = LOW;
  );
 
 if (time = (looptime/4) * 3)
  digitalWrite( 
  a = LOW;
  b = LOW;
  c = HIGH;
  d = LOW;
  );
  
 if (time = looptime) 
  digitalWrite(
  a = LOW;
  b = LOW;
  c = LOW;
  d = HIGH;
  );


Here's the next version. I fixed the millis function "time" to be unsigned long and not an int. The long can handle bigger numbers so that it won't overlap as quickly as int. In this example it won't make a difference I guess since the purpose of this code would be to go to 1000 ms and start again. Speaking of which there is no code in this version attempting to restart the millis count.

I also fixed the digitalWrite code.


void loop() {
 
 unsigned long time = millis();
 
 if (time == looptime/4) {
  digitalWrite (a, HIGH);
  digitalWrite (b, LOW);
  digitalWrite (c, LOW);
  digitalWrite (d, LOW);
 }
 if (time == (looptime/4) * 2) {
  digitalWrite (b, HIGH);
  digitalWrite (a, LOW);
  digitalWrite (c, LOW);
  digitalWrite (d, LOW);
 }
 if (time == (looptime/4) * 3) {
  digitalWrite (c, HIGH);
  digitalWrite (a, LOW);
  digitalWrite (b, LOW);
  digitalWrite (d, LOW);
 }
 if (time == looptime) {
  digitalWrite (d, HIGH);
  digitalWrite (a, LOW);
  digitalWrite (b, LOW);
  digitalWrite (c, LOW);
 }
 }
}

Here I'm changing the code so the if functions will actually check if "time" is less than a given division and (&&) greater than a given division. That way I'm giving areas of "time" values for individual LEDs.

This code does go through the LEDs in sequence but only once. The last LED stays on after this. So the problem remaining is that I need to somehow restart the sequence. The thought behind the last bit of code is that if all the other if statements are false then an else statement will zero the "time" and thus start the loop again. I will update next time when I have this sorted out.


void loop() {
 
 unsigned long time = millis();
 
 if (time <= looptime) {
 
 if (time <= looptime/4 && time >= 0) {
   digitalWrite (a, HIGH);
   digitalWrite (b, LOW);
   digitalWrite (c, LOW);
   digitalWrite (d, LOW);
 }
 if (time <= (looptime/2) && time >= (looptime/4)) {
   digitalWrite (a, LOW);
   digitalWrite (b, HIGH);
   digitalWrite (c, LOW);
   digitalWrite (d, LOW);
   
 }
 if (time <= (looptime/4)*3 && time >= looptime/2) {
  digitalWrite (a, LOW);
  digitalWrite (b, LOW);
  digitalWrite (c, HIGH);
  digitalWrite (d, LOW);
 }
 if (time >= (looptime/4)*3 && time <= looptime) {
  digitalWrite (a, LOW);
  digitalWrite (b, LOW);
  digitalWrite (c, LOW);
  digitalWrite (d,HIGH);
 }
 else (time == looptime); {
   time = zero;
 }
 }




17.2.2016 UPDATE

So it turns out I can't turn back time. I've been working on the code a lot during this time and gotten a lot of help from Bas for figuring out the steps towards this stage. I see now that it would've been great to document while doing it so that I would've gotten more detailed info on the development here.


So I started to use an index system for lighting up the LEDs. I copied it from Sparkfun exmaple Circuit #4. So here my goal was to at least get the LEDs to go in sequence with a delay.

I'll paste the entire code and add changes after that:

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

int pot = {0};

void setup() {
 
 int index = 0;
 
 for (index = 0; index <= 7; index ++)
 
 pinMode(ledPins[index], OUTPUT);
 
 
}

void loop() {
 
 int index;
 
 int wait;
 
 wait = analogRead (pot);
 
 for (index = 0; index <= 7; index ++)
 
 
 if (index == 0) {
   digitalWrite (ledPins[index], HIGH);
   delay (wait);
   digitalWrite (ledPins[index], LOW);
 }
 else if (index == 1) {
   digitalWrite (ledPins[index], HIGH);
   delay (wait);
   digitalWrite (ledPins[index], LOW);
 }
 else if (index == 2) {
  digitalWrite (ledPins[index], HIGH);
  delay (wait);
   digitalWrite (ledPins[index], LOW);
 }
 else if (index == 3) {
  digitalWrite (ledPins[index], HIGH);
  delay (wait);
   digitalWrite (ledPins[index], LOW);
 }
 else if (index == 4) {
   digitalWrite (ledPins[index], HIGH);
   delay (wait);
   digitalWrite (ledPins[index], LOW);
 }
 else if (index == 5) {
   digitalWrite (ledPins[index], HIGH);
   delay (wait);
   digitalWrite (ledPins[index], LOW);
 }
 else if (index == 6) {
  digitalWrite (ledPins[index], HIGH);
  delay (wait);
   digitalWrite (ledPins[index], LOW);
 }
 else if (index == 7) {
  digitalWrite (ledPins[index], HIGH);
  delay (wait);
   digitalWrite (ledPins[index], LOW);
 }
 else if (index == 8) {
  digitalWrite (ledPins[index], HIGH);
  delay (wait);
   digitalWrite (ledPins[index], LOW);
 }
 
}

So in void setup() it goes through the index numbers and sets the ledPins one by one to be OUTPUTs. The void loop works so that the sequence keeps going. But there's still delay between. So that's what I set out to replace with millis().

But I first wanted to adjust the speed of the sequence with a potentiometer:

if (index == 0) {
   digitalWrite (ledPins[index], HIGH);
   delay (wait);
   digitalWrite (ledPins[index], LOW);
   wait = analogRead (pot);
 }
 else if (index == 1) {
   digitalWrite (ledPins[index], HIGH);
   delay (wait);
   digitalWrite (ledPins[index], LOW);
   wait = analogRead (pot);
 }
 else if (index == 2) {
  digitalWrite (ledPins[index], HIGH);
  delay (wait);
   digitalWrite (ledPins[index], LOW);
   wait = analogRead (pot);
 }
 else if (index == 3) {
  digitalWrite (ledPins[index], HIGH);
  delay (wait);
   digitalWrite (ledPins[index], LOW);
   wait = analogRead (pot);
 }
 else if (index == 4) {
   digitalWrite (ledPins[index], HIGH);
   delay (wait);
   digitalWrite (ledPins[index], LOW);
   wait = analogRead (pot);
 }
 else if (index == 5) {
   digitalWrite (ledPins[index], HIGH);
   delay (wait);
   digitalWrite (ledPins[index], LOW);
   wait = analogRead (pot);
 }
 else if (index == 6) {
  digitalWrite (ledPins[index], HIGH);
  delay (wait);
   digitalWrite (ledPins[index], LOW);
   wait = analogRead (pot);
 }
 else if (index == 7) {
  digitalWrite (ledPins[index], HIGH);
  delay (wait);
   digitalWrite (ledPins[index], LOW);
   wait = analogRead (pot);
 }
 else if (index == 8) {
  digitalWrite (ledPins[index], HIGH);
  delay (wait);
   digitalWrite (ledPins[index], LOW);
   wait = analogRead (pot);
 }

Fun stuff! Especially after a lot of work to at least get to this point. I could've found a code that does this and more but I would have not understood it fully. This is a good way for me to learn.

So the next step blew my mind and it kinda hurt :) When you see that there's a waaaay simpler way to do the same thing.

So here's the whole void loop() doing the same thing:

void loop() {
 
 int index;
 
 int wait;
 
 for (index = 0; index <= 7; index ++) {  
   
     wait = analogRead (pot);
 
   digitalWrite (ledPins[index], HIGH);
   delay (wait);
   digitalWrite (ledPins[index], LOW);

 }
}

So since the index is checked and incremented on each loop anyway, there's no need to check it again for each LED. The index number is changed to the next one before digitalWrite and thus it'll always be the next LED in the sequence.

The next changes were due to Bas starting to lead me to a new direction. At this point I obviously didn't fully understand what's the point with having an onEvent and offEvent.

unsigned long onEvent;

unsigned long offEvent;

This loop just keeps all the LEDs on. It adds to the offEvent each time it goes to the if (onEvent <= millis() ). So it never reaches the offEvent and thusly all the LEDs stay on.

But I didn't realize this just yet cause there was another version exactly like this after this failed.

void loop() {
 
 // int wait; 
   
 // wait = analogRead (pot);
 
 if (onEvent <= millis() ) {
 
   offEvent + 500;
   
   digitalWrite (ledPins[index], HIGH);
 
 }
 
 else if (offEvent <= millis() ) {
 
   onEvent + 500;
   
   digitalWrite (ledPins[index], LOW);
   
 }

 index ++;
 
 if (index == 8) {
   index = 0;
 }

}

The next version was after I heard of this function called boolean. I knew we could use a kind of switch to better control when an if event started. But I realyl didn't know how at this point. Again I just added the function into my code without actually utilizing it for anything.

So I added this at the start of the code:

boolean switchIndex = true;

And this is where it's not really doing anything. None of the LEDs light up.

void loop() {
 
 // int wait; 
   
 // wait = analogRead (pot);
 
 unsigned long x = millis();
 
 if (onEvent <= x && switchIndex == true) {
   
   digitalWrite (ledPins[index], HIGH);
   
   offEvent = 250 + x;
 }
 
 if (offEvent <= x && switchIndex == false) {
   
   digitalWrite (ledPins[index], LOW);
   
   onEvent = 500 + x;
   
 if (offEvent > x && switchIndex == true) {
   
  index ++; 
 }
 if (index == 8) {
   
   index = 0; 
 } 
 } 
}

The next iteration is actually doing something very interesting. The boolean is now called ledOn. So the for example the first if fucntion checks if onEvent is less than x AND it checks if the boolean function ledOn is true. If both of those are true it will turn the LED on, add 1000 millis to onEvent and 500 to offEvent. In addition it will invert the state of ledOn making it false.

So in the next if function it will check if offEvent is less than x AND it will check if ledOn is not true. If so, it'll turn the LED off add an increment to the index to move on to the next LED before the next loop starts and also it'll change ledOn back to true so that the first if function is going to happen next to make the next LED turn on. And so on.

So in practice it turns the LED on for 500 ms and off for 500 ms and then does the same to the next one and around the whole sequence.

if (onEvent <= x && ledOn) {
   
   digitalWrite (ledPins[index], HIGH);
   
   onEvent = 1000 + x;
   
   offEvent = 500 + x;
 
   ledOn = !ledOn;
 
 }
 
 
 if (offEvent <= x && !ledOn) {
   
   digitalWrite (ledPins[index], LOW);
   
   index ++;
   
   ledOn = !ledOn;
   
 }

This was a turning point in this process. I knew that an important part was now done. And I really have a more deeper understanding of the code as opposed to just copying a complete code.

The next step was getting the potentiometer working so that it doesn't have to check the value everytime. Instead it would see if the potentionmeter value is different from a value it would only then go and set the new value to it. In this case I'm trying to set the rate of the notes being played (rate) and the length of the notes (note).

 int ledPins [] = {2,3,4,5,6,7,8,9};
 int index;
 int potRate = 0;
 int rateRaw;
 int rate;
 int potNote = 1;
 int noteRaw;    
 int note;    
 boolean ledOn = true;
 unsigned long onEvent;
 unsigned long offEvent;

void setup() {
 
 for (index = 0; index <= 7; index ++)
   pinMode(ledPins[index], OUTPUT);
   
 index = 0;
   
 Serial.begin(115200);
}
void loop() {
 
 int Nrate = analogRead (potRate);
 
 int Nnote = analogRead (potNote);
 
 unsigned long x = millis();
 
 if (Nrate != rateRaw); {
   
   rate = map(rateRaw, 0, 1023, 80/8, 30000/8);
   
   Nrate = rateRaw;
   Serial.print("rate: ");
   Serial.println(rate); 
 }
 if (Nnote != noteRaw); {
   
    note = map(noteRaw, 0, 1023, rate, 0);
 
    Nnote = noteRaw;
   Serial.print("note: ");
   Serial.println(note);  
 }
 if (onEvent <= x && ledOn) {
   
   onEvent = rate + x;
   offEvent = onEvent - note;

   digitalWrite (ledPins[index], HIGH);
 
   ledOn = !ledOn;
 } 
 if (offEvent <= x && !ledOn) {
   
   digitalWrite (ledPins[index], LOW);
   
   index ++;
   
   if (index == 8)
     index = 0;
   
   ledOn = !ledOn;
 }
}

This code didn't work as expected so Bas tried to debug it with the Serial funtions. I'm also using the map function for setting a scale of values the potentionmeter is affecting. So that I can set the max and min speed of the loop and notes.

I think we had just a couple of typos in there and probably something that I'm missing here. Again it would've been a good idea to document the progress while doing it so that I would now remember what it was.

But anyway this is a working version of the LED sequencer code that I was trying to achieve for all this time. It has two potentionmeters that control the length of the loop and the notes. Took me about a week or more to get to this point, with a lot of help from Bas. Dank je, Bas!

 int ledPins [] = {2,3,4,5,6,7,8,9};
 
 int index;
 
 int potRate = 0;
 int rateRaw;
 int rate;
 
 int potNote = 1;
 int noteRaw;    
 int note;    
 
 boolean ledOn = true;
 
 unsigned long onEvent;
 
 unsigned long offEvent;
 

void setup() {
 
 for (index = 0; index <= 7; index ++)
   pinMode(ledPins[index], OUTPUT);
   
 index = 0;
}


void loop() {
 
 
 int Nrate = analogRead (potRate);
 
 int Nnote = analogRead (potNote);
 
   
 unsigned long x = millis();
 
 
 if (Nrate != rateRaw); {
   
   rate = map(rateRaw, 0, 1023, 80/8, 30000/8);
   
   rateRaw = Nrate;
   
 }
 
 if (Nnote != noteRaw); {
   
    note = map(noteRaw, 0, 1023, rate, 0);
 
    noteRaw = Nnote;
    
 }
 
 if (onEvent <= x && ledOn) {
   
   onEvent = rate + x;
   offEvent = onEvent - note;

   digitalWrite (ledPins[index], HIGH);
 
   ledOn = !ledOn;
 
 } 
 
 if (offEvent <= x && !ledOn) {
   
   digitalWrite (ledPins[index], LOW);
   
   index ++;
   
   if (index == 8)
     index = 0;
   
   ledOn = !ledOn;
 }

}

Well now that I have them blinking in sequence, let's make them beep in sequence. Making them beep the same note that I can control with a pot was pretty easy to do. Basically just add a speaker to one pin, potentiometer to another and add a tone function to the ledOn event and noTone to the ledOff event.

...

int speaker = 13;
int potPitch = A3;

...

int pitch = analogRead (potPitch);
if (onEvent <= x && ledOn) {
   
   onEvent = rate + x;
   offEvent = onEvent - note;
   digitalWrite (ledPins[index], HIGH);
   
   tone(speaker, pitch);
 
   ledOn = !ledOn;
 } 
 if (offEvent <= x && !ledOn) {
   
   digitalWrite (ledPins[index], LOW);
   
   noTone(speaker);

...

So that's fun and annoying at the same time! You get to play with the pitch and the length of the notes and the loop. This is the best music I've heard for ages! Some of my co-workers seem to disagree.

Next step will be tidying up the circuit board. It gets a bit messy with the amount of wires in it.

While cleaning up I arranged components to make it a bit more logical and at the same time I took the time to actually draw a schematic. This was super-easy with Fritzing. It's an open-source program and it was ridiculously easy to learn. I didn't see a tutorial or any help to learn all the basics. It's very intuitive. And now I have the schematics ready for documenting all the changes I need to make in the circuit.

All previous codes should work with this circuit even though some of the components aren't used in the beginning.


After cleaning up I'll try to assign different notes to each step of the sequence.




18.2.2016 UPDATE

Next problem! How to change the pitch of individual notes?

So want to have a potentiometer and 8 buttons for each note. When I press a button the pitch set by the potentiometer will be saved to that note.

I can use the existing index system that I used for the LED sequence and copy the LED array to make the control system for the tones.

Maybe... Time to try it out.




19.2.2016 UPDATE

Not much progress yesterday. Open hours happened.

So my mission now is to get one button to change a fixed pitch for all notes.

I figured I would just add a button INPUT and use the digitalRead of that value to work as a condition in the if function that changes the pitch. Well I've been trying to get it to work but I either get no pitch control at all or nothing special happening at all. So there's something wrong with the way I'm approaching this. At the moment I'll just wait for a certain someone to help me :)

So maybe if I can't get this to work before weekend I should just concentrate on the actual casing for the project.




9.3.2016 UPDATE

Schedule has been stretching but there has been a lot of progress. Documentation could've been more immediate to capture what I actually learned but the learning part was enough load for my brain at the moment so now I just go back and try to pick up the pieces.

I have now a multiplex LED system doing the same thing as before but with less pins being used. You can google multiplex LED to get a general idea on what sort of circuit is being used here.

The same method is used for the buttons and that way I have 6 pins for 8 LEDs, 6 pins for 8 buttons and that leaves me enough pins for 4 potentiometers and a speaker. There's even a couple of pins left for expanded versions.

Here's the circuit as it is now, made with Fritzing:



It's actually much messier in real life.

The latest version of the code looks like this:


 int ledPins [][2] = {
                     {2,5},
                     {2,6},
                     {2,7},
                     {3,5},
                     {3,6},
                     {3,7},
                     {4,5},
                     {4,6}
                   };  
 int steps [] = {440,440,440,440,440,440,440,440};    
 int index;  
 int speaker = 1;  
 int toneButton = 8;
 int buttonState =-1;  
 int potPitch = A2;
 int pitchChange;
 int pitch;  
 int potRate = A0;
 int rateChange;
 int rate;  
 int potNote = A1;
 int noteChange;    
 int note;      
 boolean ledOn = true;  
 unsigned long onEvent;  
 unsigned long offEvent;

void setup() {  
 pinMode (toneButton, INPUT_PULLUP);    
 index = 0;  
}

void loop() {  
 int Nrate = analogRead (potRate);  
 int Nnote = analogRead (potNote);  
 int Npitch = analogRead (potPitch);  
 int Nbutton = buttonScan ();    
 int stepsTone;    
 unsigned long x = millis();
 
 if (Nrate != rateChange); {    
   rate = map(rateChange, 0, 1023, 1, 30000/8);    
   rateChange = Nrate;    
 }
 
 if (Nnote != noteChange); {    
    note = map(noteChange, 0, 1023, rate, 0);  
    noteChange = Nnote;      
 }
 
 if (Npitch != pitchChange); {    
    pitch = map(pitchChange, 0, 1023, 20, 20000);  
    pitchChange = Npitch;     
 }  

 if (Nbutton!=-1)
   steps[Nbutton] = pitch;  
 
 if (onEvent <= x && ledOn) {    
   onEvent = rate + x;
   offEvent = onEvent - note;    
   tone (speaker, steps[index]); 
   turnledon (index);  
   ledOn = !ledOn;  
 } 
 
 if (offEvent <= x && !ledOn) {    
   noTone(speaker);    
   turnledsoff ();    
   index ++;
   
   if (index == 8)
     index = 0;    
   ledOn = !ledOn; 
 }  
}

void turnledon(int id) {
 turnledsoff();
 pinMode(ledPins[id][0], OUTPUT);
 pinMode(ledPins[id][1], OUTPUT);
 digitalWrite(ledPins[id][1], HIGH);
}

void turnledsoff() {
 for (int i = 2; i < 8; i++)
   pinMode(i, INPUT);
}

int buttonScan () {
 if (digitalRead(8) == LOW)
    return 0;
   else
    return -1;
}

So now I can change the first note but all the others will always be 440 Hz as set in the start of the code. When I press the button it will change the first note to what ever value the potentiometer is at.