For loop not exiting at condition

Introduction: I am a first timer programming an Arduino. I have basic programming knowledge, nothing modern.

The reason for getting a few Arduino's besides having fun is that I'm doing the CarbageRun again next year (february 2021, driving to the polar circle!), and want to automate some stuff. My first goal is to play some custom melodies on our 6-tone-trainhorn. The trainhorns are controlled by solenoids.

The basic scheme I'm aiming for: Arduino-with-buttons --> 6-relaisboard --> 12v solenoids.
When I press button A, play melody A, etcetera.

I've written some basic code for the purpose of testing. I'll use the 6 Analog-in's as OUTPUT for controlling the relaisboard (matches perfect with the number of horns to control). For the testcase I've wired up 2 LEDs to A0 and A1, and two push-buttons to pin 3 & 4 (and did all the necessary stuff like pulldown resistors etc).

(The code, I can imagine, could be done better - for example: is there a way to make a variable-variable? For example, have several variables named Var1, Var2, Var3 etc and then call them in a loop by "Var"&i or something similar?)

Anyhow, the problem. In the code below, when I FIRST press the white button, it perfectly plays the white 'melody' (in this case, blinks my 2 test-LED's in the correct way). If I then press the red button, that one plays perfectly as well. If I would push the white button again, it plays the 'white button melody', and then the last note of the 'red button melody'. Also, iIt does not matter which values I enter for "SongLength" - if I would make SongLength=3 for the red-button-melody it would still play out all the notes, meaning that in the for-loop i goes all the way up untill at least 8 before exiting.

What obvious thing am I missing here?

const int buttonredPin = 3;     // The red button
const int buttonwhiPin = 4;     // The white button
const int Tone1 = A0;
const int Tone2 = A1;
const int Tone3 = A2;
const int Tone4 = A3;
const int Tone5 = A4;
const int Tone6 = A5;
const int Tone0 = 13; // internal LED


int buttonredState = 0;         // variables for reading the pushbutton status
int buttonwhiState = 0;
int SongLength = 0;
int Note1 = 0; // variables for holding the tone to play
int Note2 = 0;
int Note3 = 0;
int Note4 = 0;
int Note5 = 0;
int Note6 = 0;
int Note7 = 0;
int Note8 = 0;
int Note9 = 0;
int Note10 = 0;
int Note11 = 0;
int Note12 = 0;
int Note13 = 0;
int Note14 = 0;
int Note15 = 0;
int Note16 = 0;
int Note17 = 0;
int Note18 = 0;
int Note19 = 0;
int Note20 = 0;

int NoteL1 = 0; // variables for holding how long the tone should play
int NoteL2 = 0;
int NoteL3 = 0;
int NoteL4 = 0;
int NoteL5 = 0;
int NoteL6 = 0;
int NoteL7 = 0;
int NoteL8 = 0;
int NoteL9 = 0;
int NoteL10 = 0;
int NoteL12 = 0;
int NoteL13 = 0;
int NoteL14 = 0;
int NoteL15 = 0;
int NoteL16 = 0;
int NoteL17 = 0;
int NoteL18 = 0;
int NoteL19 = 0;
int NoteL20 = 0;

int NoteNow = 0; // the current tone to play
int NoteLNow = 0; // the length of that current tone
int i; // the loop integer


void setup() {
  pinMode(buttonredPin, INPUT);
  pinMode(buttonwhiPin, INPUT);
  pinMode(Tone1, OUTPUT);
  pinMode(Tone2, OUTPUT);
  pinMode(Tone3, OUTPUT);
  pinMode(Tone4, OUTPUT);
  pinMode(Tone5, OUTPUT);
  pinMode(Tone6, OUTPUT);
  pinMode(Tone0, OUTPUT); // internal LED
}

void loop() {

  buttonredState = digitalRead(buttonredPin);
  buttonwhiState = digitalRead(buttonwhiPin);

  if (buttonredState == HIGH) {
    SongLength = 8;
    Note1 = Tone1;
    NoteL1 = 250;
    Note2 = Tone0;
    NoteL2 = 250;
    Note3 = Tone1;
    NoteL3 = 250;
    Note4 = Tone0;
    NoteL4 = 250;
    Note5 = Tone1;
    NoteL5 = 250;
    Note6 = Tone0;
    NoteL6 = 250;
    Note7 = Tone2;
    NoteL7 = 2000;
    Note8 = Tone0;
    NoteL8 = 2000;

    goto play;
  } else if (buttonwhiState == HIGH) {

    SongLength = 3;
    Note1 = Tone1;
    NoteL1 = 100;
    Note2 = Tone2;
    NoteL2 = 100;
    Note3 = Tone1;
    NoteL3 = 100;
    Note4 = Tone2;
    NoteL4 = 100;
    Note5 = Tone0;
    NoteL5 = 1000;
    goto play;
  } else {
    SongLength = 0;
  }


play:
  for (i = 0; i < SongLength; i++) {

    if (i = 1) {
      NoteNow = Note1;
      NoteLNow = NoteL1;
      digitalWrite(NoteNow, HIGH);
      delay(NoteLNow);
      digitalWrite(NoteNow, LOW);

    }
    if (i = 2) {
      NoteNow = Note2;
      NoteLNow = NoteL2;
      digitalWrite(NoteNow, HIGH);
      delay(NoteLNow);
      digitalWrite(NoteNow, LOW);

    }
    if (i = 3) {
      NoteNow = Note3;
      NoteLNow = NoteL3;
      digitalWrite(NoteNow, HIGH);
      delay(NoteLNow);
      digitalWrite(NoteNow, LOW);

    }
    if (i = 4) {
      NoteNow = Note4;
      NoteLNow = NoteL4;
      digitalWrite(NoteNow, HIGH);
      delay(NoteLNow);
      digitalWrite(NoteNow, LOW);

    }
    if (i = 5) {
      NoteNow = Note5;
      NoteLNow = NoteL5;
      digitalWrite(NoteNow, HIGH);
      delay(NoteLNow);
      digitalWrite(NoteNow, LOW);

    }
    if (i = 6) {
      NoteNow = Note6;
      NoteLNow = NoteL6;
      digitalWrite(NoteNow, HIGH);
      delay(NoteLNow);
      digitalWrite(NoteNow, LOW);

    }
    if (i = 7) {
      NoteNow = Note7;
      NoteLNow = NoteL7;
      digitalWrite(NoteNow, HIGH);
      delay(NoteLNow);
      digitalWrite(NoteNow, LOW);

    }
    if (i = 8) {
      NoteNow = Note8;
      NoteLNow = NoteL8;
      digitalWrite(NoteNow, HIGH);
      delay(NoteLNow);
      digitalWrite(NoteNow, LOW);

    }
    if (i = 9) {
      NoteNow = Note9;
      NoteLNow = NoteL9;
      digitalWrite(NoteNow, HIGH);
      delay(NoteLNow);
      digitalWrite(NoteNow, LOW);

    }
    if (i = 10) {
      NoteNow = Note10;
      NoteLNow = NoteL10;
      digitalWrite(NoteNow, HIGH);
      delay(NoteLNow);
      digitalWrite(NoteNow, LOW);

    }

  }

}

Extra explanation - I used the internal led (pin 13) as the 'pause' between notes, for debugging purposes) - it is called Tone0 in my code

You need to post your code using a code block. Posting it in your text is causing weird characters.

To make your code neater the concept you need is the array. See array - Arduino Reference

So you'll have something like a notes[] array for each tune and use a for loop to move along the array entries.

Steve

A few things:

  1. Use of goto is not acceptable. Besides, the way you have it coded it is unnecessary.
  2. '=' is for assignment and '==' is for comparison. You need to fix your if statements
  3. When playing the song you should test for i equals 0 thru 9 not 1 thru 10. As you have it coded i will never equal 10

Thnx all so far - I've fixed the OP, and will implement the use of an array, should turn out way neater and increase ease of modification.

  1. When playing the song you should test for i equals 0 thru 9 not 1 thru 10. As you have it coded i will never equal 10

Why would it never equal 10 (or higher)? It's a 16 bit integer, it should be able to go all the way up, to, 32667 if I would allow the condition of the loop to go up that far? For example, if I would set SongLength=10 and then enter the for-loop, it would go up to 10 and then end the for-loop?

icerunner:
Why would it never equal 10 (or higher)? It's a 16 bit integer, it should be able to go all the way up, to, 32667 if I would allow the condition of the loop to go up that far? For example, if I would set SongLength=10 and then enter the for-loop, it would go up to 10 and then end the for-loop?

Nope

  for (i = 0; i < SongLength; i++) {

The loop iterates from 0 to 9 if SongLength=10. When i = 10 then i is no longer less than SongLength so the loop exits and does not execute the loop code.

ToddL1962:
Nope

  for (i = 0; i < SongLength; i++) {

The loop iterates from 0 to 9 if SongLength=10. When i = 10 then i is no longer less than SongLength so the loop exits and does not execute the loop code.

Sorry, wrong example, forgot that I switched the less-or-equal for just less-then. Let's say I'll write a third melody for the yet-to-come third button, and it'll have SongLength=20.
The code is still work-in-progress, obviously, I've grown some parts of it to fit longer melodies, yet not all parts (as you can see, I've already, set up the vars up to Note20 etc).

Did read up on your never-useggoto-note though. Sorry for that one, will never happen again sir

icerunner:
Sorry, wrong example, forgot that I switched the less-or-equal for just less-then. Let's say I'll write a third melody for the yet-to-come third button, and it'll have SongLength=20.
The code is still work-in-progress, obviously, I've grown some parts of it to fit longer melodies, yet not all parts (as you can see, I've already, set up the vars up to Note20 etc).

Did read up on your never-useggoto-note though. Sorry for that one, will never happen again sir

No problem. That's how we learn!

And as a reply to nyself and future visitors - my error, as pointed out above (= vs ==), is explained here:

https://forum.arduino.cc/index.php?topic=40510.0

In short: my "if (i = 7)" actually sets i to 7 and then checks for the condition that i does not equal zero...
Should have used ==

For informative purposes, and for those wanting to bluntly give critique and point out the terrible errors and style flaws in my code, I'm presenting my updated code. I'm making more use of loops, and made the code adjustable to account for more buttons or more horns, when needed.
Next on the todo-list: make it more easy to enter songs, and enable multiple horns to play at the sme time.

int Horns[] = {   // an array of pin numbers to which horns are attached. Horns[0] is the builtin led, pin 13, used for debugging / pauses (blank notes)
  13, 14, 15, 16, 17, 18, 19
};
int hornCount = 7;                                                // number of horns, inlcuding the builtin led

int Buttons[] = {3, 4};                                           // array for pin numbers where push-buttons are attached
int buttonCount = 2;                                              // number of buttons

int buttonPressed; // whill hold the number of the button pressed, corresponding to the Buttons[] array

int SongN[40] = {}; // The horn numbers in chronological order to play
int SongL[40] = {}; // The amount of time that the corresponding horn should play
int noteCount = 0;           // the number of notes to play (i.e. the length of the array)

char currentHorn = A1;
int currentLength;

//SONGS TO PLAY
//Song 0 = Test song
int  Song0N[] = {
  1, 3, 5, 3, 1, 3, 5, 3, 1, 3, 5, 3, 6, 0
};
int  Song0L[] = {
  500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 1000
};
int  Song0C = 14;
//----------------------------------------------


//Song 1 = Test song
int  Song1N[] = {
  1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1, 6, 3
};
int  Song1L[] = {
  500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 1000, 1000
};
int  Song1C = 13;
//----------------------------------------------


void setup() {
  for (int thisPin = 0; thisPin < hornCount; thisPin++) {
    pinMode(Horns[thisPin], OUTPUT);
  }
  for (int thisPin = 0; thisPin < buttonCount; thisPin++) {
    pinMode(Buttons[thisPin], INPUT);
  }
}

void loop() {


  for (int thisButton = 0; thisButton < 1000; thisButton++) {
    if (thisButton > buttonCount) {
      thisButton = 0;
    }
    int buttonstate = digitalRead(Buttons[thisButton]);
    if (buttonstate == HIGH) {
      buttonPressed = thisButton;
      thisButton = 1000;
    }
  }

  if (buttonPressed == 0) {
    //copy song 0 to play-arrays
    noteCount = Song0C;
    for (int thisNote = 0; thisNote <= noteCount; thisNote++) {
      SongN[thisNote] = Song0N[thisNote];
      SongL[thisNote] = Song0L[thisNote];
    }
  }

  else if (buttonPressed == 1) {
    //copy song 1 to play array
    noteCount = Song1C;
    for (int thisNote = 0; thisNote <= noteCount; thisNote++) {
      SongN[thisNote] = Song1N[thisNote];
      SongL[thisNote] = Song1L[thisNote];
    }

  }



  // loop from the first note to the last:
  for (int thisNote = 0; thisNote < noteCount; thisNote++) {

    currentHorn = SongN[thisNote]; // get the current horn to play
    currentLength = SongL[thisNote]; // get the current length that the horn should play

    // play the horn:
    digitalWrite(Horns[currentHorn], HIGH);
    delay(currentLength);
    // turn the pin off:
    digitalWrite(Horns[currentHorn], LOW);

  }
  noteCount = 0;
}

I think you're overworking the for loop here:

  for (int thisButton = 0; thisButton < 1000; thisButton++) {
    if (thisButton > buttonCount) {
      thisButton = 0;
    }
    int buttonstate = digitalRead(Buttons[thisButton]);
    if (buttonstate == HIGH) {
      buttonPressed = thisButton;
      thisButton = 1000;
    }
  }

It is only under exceptional, absolutely necessary conditions that you modify a for loop variable, inside the for loop. Even in such cases, it is not done multiple times. What you should do, is unravel this logic so that it consists of whatever standard nested loops are needed to do the same thing. Sometimes you can move some stuff into the control statements themselves, if you have to.

Also you need to start commenting your code.

aarg:
I think you're overworking the for loop here:

  for (int thisButton = 0; thisButton < 1000; thisButton++) {

if (thisButton > buttonCount) {
      thisButton = 0;
    }
    int buttonstate = digitalRead(Buttons[thisButton]);
    if (buttonstate == HIGH) {
      buttonPressed = thisButton;
      thisButton = 1000;
    }
  }



It is only under exceptional, absolutely necessary conditions that you modify a for loop variable, inside the for loop. Even in such cases, it is not done multiple times. What you should do, is unravel this logic so that it consists of whatever standard nested loops are needed to do the same thing. Sometimes you can move some stuff into the control statements themselves, if you have to.

Also you need to start commenting your code.

Code commented - just figured out the same thing...
Reason for modifying the loop is to stay in the buttton-read loop. If there is no button being pressed, of course I could still let it run past the rest of the code, eventually ending up back at the button-read-loop without playting anything, yet that would mean that for a very (VERY) short period of time the buttons are not being read... or is that indeed worrying about a detail that's so small that it won't matter?

int Horns[] = {13, 14, 15, 16, 17, 18, 19};   // array of pins to which horns are attached. Horns[0] is the builtin led, used for debugging / pauses (blank notes)

int hornCount = 7;                            // number of horns, inlcuding the builtin led

int Buttons[] = {3, 4};                       // array for pin numbers where push-buttons are attached
int buttonCount = 2;                          // number of buttons

int buttonPressed;                            // whill hold the number of the button pressed, corresponding to the Buttons[] array

int SongN[40] = {};                           // The horns to play, in a row
int SongL[40] = {};                           // The amount of time that the horn should play
int noteCount = 0;                            // the number of notes to play (i.e. the length of the song-arrays)

int currentHorn;                              // holds the horn to play while playing
int currentLength;                            // holds the length that the currentHorn should play

//----------------------------------------------
//SONGS TO PLAY
//----------------------------------------------
//Song 0 = Test song
int  Song0N[] = {3, 0, 3, 0, 2, 0, 4, 0, 3, 0, 2, 0, 0};
int  Song0L[] = {250, 250, 250, 250, 250, 250, 250, 250, 750, 250, 750, 250, 10};
int  Song0C = 13;
//----------------------------------------------
//Song 1 = Test song
int  Song1N[] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
int  Song1L[] = {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000};
int  Song1C = 11;
//----------------------------------------------


void setup() {
  for (int thisPin = 0; thisPin < hornCount; thisPin++) {
    pinMode(Horns[thisPin], OUTPUT);
  }
  for (int thisPin = 0; thisPin < buttonCount; thisPin++) {
    pinMode(Buttons[thisPin], INPUT);
  }
}

void loop() {


  for (int thisButton = 0; thisButton < 1000; thisButton++) {   // read the buttons
    if (thisButton > buttonCount) {         // if we've read all the buttons, go back to read out the first
      thisButton = 0;
    }
    int buttonstate = digitalRead(Buttons[thisButton]);
    if (buttonstate == HIGH) {    // if a button-press is detected, mark down which button, and exit the for-loop
      buttonPressed = thisButton;
      thisButton = 1000;       // probably will never make a 1000-button-setup, looks like a safe exit-condition
    }
  }

  if (buttonPressed == 0) {        // copy song 0 to the play-arrays
    noteCount = Song0C;
    for (int thisNote = 0; thisNote <= noteCount; thisNote++) {
      SongN[thisNote] = Song0N[thisNote];
      SongL[thisNote] = Song0L[thisNote];
    }
  }

  else if (buttonPressed == 1) {                                // copy song 1 to the play-arrays
    noteCount = Song1C;
    for (int thisNote = 0; thisNote <= noteCount; thisNote++) {
      SongN[thisNote] = Song1N[thisNote];
      SongL[thisNote] = Song1L[thisNote];
    }

  }

  for (int thisNote = 0; thisNote < noteCount; thisNote++) {    // play time - play the song that is copied!

    currentHorn = SongN[thisNote]; // get the current horn to play
    currentLength = SongL[thisNote]; // get the current length that the horn should play

    // play the horn:
    digitalWrite(Horns[currentHorn], HIGH);
    delay(currentLength);
    // turn the pin off:
    digitalWrite(Horns[currentHorn], LOW);

  }
  noteCount = 0;
}

Hi,
What model Arduino are you using?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks.. Tom... :slight_smile:

TomGeorge:
Hi,
What model Arduino are you using?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks.. Tom... :slight_smile:

I couldn't find different resistors that quick within Fritzing, so don't look at the resistorvalues (the buttons are connnected to ground with 10k Ohm, the LED's with 220 Ohm)

  for (int thisButton = 0; thisButton < 1000; thisButton++) {   // read the buttons
    if (thisButton > buttonCount) {         // if we've read all the buttons, go back to read out the first
      thisButton = 0;
    }
    int buttonstate = digitalRead(Buttons[thisButton]);
    if (buttonstate == HIGH) {    // if a button-press is detected, mark down which button, and exit the for-loop
      buttonPressed = thisButton;
      thisButton = 1000;       // probably will never make a 1000-button-setup, looks like a safe exit-condition
    }
  }

Sorry, that is still some extremely convoluted code. Why not just directly code the thing you want to do?

  for (int thisButton = 0; thisButton < buttonCount; thisButton++) {
    int buttonstate = digitalRead(Buttons[thisButton]);   // read the button
    if (buttonstate == HIGH) {    // if a button-press is detected,
      buttonPressed = thisButton; // mark down which button,
      break;  //  and exit the for-loop
    }
  }

See, you do NOT have to modify any for loop variables for this.

aarg:

  for (int thisButton = 0; thisButton < 1000; thisButton++) {   // read the buttons

if (thisButton > buttonCount) {        // if we've read all the buttons, go back to read out the first
      thisButton = 0;
    }
    int buttonstate = digitalRead(Buttons[thisButton]);
    if (buttonstate == HIGH) {    // if a button-press is detected, mark down which button, and exit the for-loop
      buttonPressed = thisButton;
      thisButton = 1000;      // probably will never make a 1000-button-setup, looks like a safe exit-condition
    }
  }




Sorry, that is still some extremely convoluted code. Why not just directly code the thing you want to do?



for (int thisButton = 0; thisButton < buttonCount; thisButton++) {
    int buttonstate = digitalRead(Buttons[thisButton]);  // read the button
    if (buttonstate == HIGH) {    // if a button-press is detected,
      buttonPressed = thisButton; // mark down which button,
      break;  //  and exit the for-loop
    }
  }




See, you do NOT have to modify any for loop variables for this.

In your example, the for-loop will exit and continue the rest of the code after it polled all the buttons once (buttonCount = the total number of installed buttons); The rest of the code won't do much since no buttonpress is detected, but still - it will run, and create a very short interval where no buttons are being read, right?

A solution would be to add a line at the end of the for-loop, like

 if (thisButton = buttonCount) {thisButton==0}; // Reset the for-loop to the first button if we've read the last

Or is this a solution for a non-existent problem?

Hi,

The rest of the code won't do much since no buttonpress is detected, but still - it will run, and create a very short interval where no buttons are being read, right?

An imperceptible interval.
You need to stop over thinking and try it.

Tom.... :slight_smile:

In your example, the for-loop will exit and continue the rest of the code after it polled all the buttons once (buttonCount = the total number of installed buttons)

That is not correct. It will exit as soon as a button is detected, because of the 'break;' statement. It's frustrating that your first attempt at a solution was to modify a for loop variable!

aarg:
That is not correct. It will exit as soon as a button is detected, because of the 'break;' statement. It's frustrating that your first attempt at a solution was to modify a for loop variable!

Thnx for taking me so serious that my code frustrates you - I can hardly offended. I've taken up coding since 2 days, with my 'daytime' job being a medical doctor. Until april the 7th, my last experience with coding was around 2009, and it was in Qbasic for nostalgic reasons :slight_smile: So far my main learning point is that I quickly need to ditch a lot of the things I picked up back then. The no-nonsense way of replying on this forum might be a bit hars but is really helpfull though.

I see now my example was incomplete: I ment the example for the situation when I do not press any button - it will then exit after polling both buttons and continue the rest of the code. But as Tom said:

An imperceptible interval.
You need to stop over thinking and try it.

Probably spot on...