1

I'm trying to create a 2D array of notes that are split into 3 scales. There are 3 notes in each scale. There are 3 buttons to play each note and I want to use an available potentiometer to select each of the scales to be played.

Despite my searching I cannot understand how to achieve this correctly. So far this is what I have.

Using an Arduino Uno:

// musical notes
int C = 1046;
int D = 1175;
int E = 1319;
int F = 1397;
int G = 1598;
int A = 1760;
int B = 1976;
int C1 = 2093;
int D1 = 2349;

const int numScales = 3;
const int numNotesPerScale = 3;

const int  notes[numScales][numNotesPerScale] = {
  {C, D, E},
  {F, G, A},
  {B, C1, D1}  
};


const int numberOfButtons = 3;
int buttonPin[numberOfButtons] = {2, 7, 4};
//int notes[numberOfButtons] = {C, D, E};
int ledPin[numberOfButtons] = {11, 10, 9};

int buttonState = 0;         // variable for reading the pushbutton status
int speaker = 3;                 // name of the speaker key

void setup() {

  for (int i = 0; i < numberOfButtons; i++) {
    pinMode(buttonPin[i], INPUT);
    pinMode(ledPin[i], OUTPUT);
  }
  pinMode(speaker, OUTPUT);     // set speaker to be an output
}

void loop() {
  int numScales = map(analogRead(A2), 0, 1023, 0, 2);
  //int numScales = analogRead(A2 / 341);

  for (int i = 0; i < numberOfButtons; i++) {
    checkButton(buttonPin[i], notes[i], ledPin[i]);
  }
}

void checkButton(int buttonPin, int note, int ledPin)
{
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == HIGH){ 
    digitalWrite(ledPin, HIGH);
    tone(speaker, note);                 // play the note 
    delay(100);                       // wait for 1/10th of a second
  }  } else {
    digitalWrite(ledPin, LOW);
    noTone(speaker);                  // stop playing the note
  }
}
3
  • 5
    Which part are you having trouble with? You'll want to handle this piece by piece. Are you able to read the pot? Are you able to play notes?
    – Justin
    Commented Dec 18, 2019 at 21:13
  • 2
    what is it that you are unable to understand? ... create 2d array, read button status, read pot position, or something else? ... you need to determine what is the actual problem ... nobody can help you until you know what you need help with
    – jsotola
    Commented Dec 18, 2019 at 21:17
  • I think my pot is not being recognised and that my 2d array is being accessed like a single array instead. This is why I'm not getting any true sequence of notes. My question is how should I take "int numScales = map(analogRead(A2), 0, 1023, 0, 2);" and use it to access the scales? Commented Dec 18, 2019 at 21:21

2 Answers 2

3

Using the Arduino map() function seems a reasonable approach to this, however you have a problem with the indexing.

if (numScales == 3); {

Is probably not what you want, as that condition can never be true since you set it to something in the range of 0-2 before you reach this check.

Considering only the relevant dimension, you a 3-element array, which will have legal elements numbers 0, 1, and 2

You call map(analogRead(A2), 0, 1023, 0, 2); which can produce an output in the range of 0-2, so far so good.

But then check for a value 3 which is not only illegal as in index to a 3-element array, but unable to be produced by your code.

Additionally you should probably stop trying to use the numScales variable to do double duty holding both the initial size and later also the current index. And you should probably pass that as an argument, rather than make it a global variable.

You are also failing to ever use the current scale as an index into the notes array. The syntax of double array indexing would be notes[x][y] but of course this should only be done with indicies to elements that exist.

It would probably also be a good idea to add some debug output of the internal logic, for example print both the raw ADC reading and the result of mapping that to the selected "scale".

5
  • I'm afraid being crap at programming isn't really allowing me to absorb some of these answers correctly. If I remove "int numScales = map(analogRead(A2), 0, 1023, 0, 2);" and uncomment "//int notes[numberOfButtons] = {C, D, E};" then each button will play a C, D or E. This works fine until my decision to add a way of changing scales. Is a 2d array the best way to do this and is this a very difficult issue? Commented Dec 18, 2019 at 22:04
  • A 2d array isn't unreasonable, use the indexing syntax given in this post and make sure your indicies are legal though. Maybe first make a version which print out the indicies it would use, but doesn't actually use them. Experiment with your buttons and knob and see that what it would do is reasonable. Then add making it actually do it. Commented Dec 18, 2019 at 22:13
  • You could also flatten an array[x][y] to something like array[x+(3*y)] Commented Dec 18, 2019 at 22:14
  • The mere mention of reasonableness scares me!!!! Commented Dec 19, 2019 at 1:06
  • How do I add my updated code for analysis and further comment please? Commented Dec 19, 2019 at 21:36
1
// musical notes
int C = 1046;
int D = 1175;
int E = 1319;
int F = 1397;
int G = 1598;
int A = 1760;
int B = 1976;
int C1 = 2093;
int D1 = 2349;

const int columns = 3;
const int scales = 3;
int potVal = 0;
const int  notes[scales][columns] = {
  {C, D, E},
  {F, G, A},
  {B, C1, D1}
};

const int numberOfButtons = 3;
int buttonPin[numberOfButtons] = {2, 7, 4};
int ledPin[numberOfButtons] = {11, 10, 9};

int buttonState = 0;         // variable for reading the pushbutton status
int speaker = 3;                 // name of the speaker key

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

  for (int i = 0; i < numberOfButtons; i++) {
    pinMode(buttonPin[i], INPUT);
    pinMode(ledPin[i], OUTPUT);
  }
  pinMode(speaker, OUTPUT);     // set speaker to be an output
}

void loop() {
  int potVal = map(analogRead(A2), 0, 1024, 0, 3);
  Serial.println(notes[2][2]);
  for (int i = 0; i < numberOfButtons; i++) {
    //checkButton(buttonPin[i], notes[scales][columns], ledPin[i]);
    checkButton(potVal,i);
  }
}

void checkButton(int scaleNum, int buttonNum)
{
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin[buttonNum]);

  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
    digitalWrite(ledPin[buttonNum], HIGH);
    tone(speaker, notes[scaleNum][buttonNum]);                 // play the     note
    delay(100);                       // wait for 1/10th of a second
  } else {
    digitalWrite(ledPin[buttonNum], LOW);
    noTone(speaker);                  // stop playing the note
  }
}
3
  • Ok, thank you everyone for your help! Here is the completed and functioning code for anyone that may find it useful. Why is it so hard to upload code on this site? Commented Dec 20, 2019 at 15:57
  • This code is incorrect. As previously explained, 3 is not a legal index into a 3 element array. You must return the maximum of your map function to 2 as it was before, not 3. Commented Dec 20, 2019 at 16:19
  • Sorry Chris, I keep getting stick for that one but it was pointed out to me recently that doing this creates an even spread across the pot range and in actual fact never produces a value of 3. I had it the old way mapped to 2 using a maximum of 1023 and it creates problems because 1 extends almost right to the end of the pots range and keeps alternating with 2 so no stability. ......."1023/1024 is 2.9999 something and so gets rounded down to 2. Mapping to 0 to 2 would mean that only the very last position would map to 2". Commented Dec 20, 2019 at 20:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.