Help to control the number of times the "loop" is implemented

elp, please.
Old computer user but Arduino newbie.
I've been developing a sketch to control a dc motor to drive a carriage back and forth along a track, between limit switches.
The setup part starts with a button push, activates a relay, waits 5 seconds, starts the motor in a specic direction towards the limit switch then proceeds to the loop setion.
In the loop te first part generates a pair of random numbers which go to control the speed of the motor in each direction, changing on each repetion of the loop and reversing the motor each time a limit swith is activated. So far so good. the attached sketch does all of this. Where I am not succeeding is trying to define the number of times the back and forth motion happens. I've tried "for" loops and "while" loops without success. This is where I will be grateful for any suggestions or guidance. If possible, I would also like to reset at this point so the whole process can be repeated by pressing the start button.
For information, the sketch is based on a tutorial in the arduino forum and I've corrected a few errors to get to this stage. It uses the ezbutton library and I believe it was created by the author of the library. I'm using a UNO R4 Wifi and a motor shield. I spent several days trying to the debugger working, so I could see what was happening and so put the "for" or "while" in the right place. That is an exercise in frustration.



#include <ezButton.h>


#define DIRECTION_CCW -1
#define DIRECTION_CW 1

#define ENA_PIN 3  // The Arduino pin connected to the EN1 pin L298N
#define IN1_PIN 12  // The Arduino pin connected to the IN1 pin L298N
//#define IN2_PIN 9  // The Arduino pin connected to the IN2 pin L298N

//define relay pin here

ezButton limitSwitch_1(4);  // create ezButton object that attach to pin 4
ezButton limitSwitch_2(5);  // create ezButton object that attach to pin 5
ezButton button(7);  // create ezButton object that attach to pin 7;

int direction = DIRECTION_CW;
int prev_direction = DIRECTION_CW;

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

  limitSwitch_1.setDebounceTime(50);  // set debounce time to 50 milliseconds
  limitSwitch_2.setDebounceTime(50);  // set debounce time to 50 milliseconds
  button.setDebounceTime(50); // set debounce time to 50 milliseconds

   int seed = analogRead(0);

 randomSeed(seed);

 
 while (!button.isPressed())
   button.loop(); // MUST call the loop() function first 
 
  // initialize digital pins as outputs.
  pinMode(ENA_PIN, OUTPUT);
  pinMode(IN1_PIN, OUTPUT);
  //pinMode(IN2_PIN, OUTPUT);
 //insert relay command here
  delay(5000);

  analogWrite(ENA_PIN, 250);  // max speed
  digitalWrite(IN1_PIN, HIGH);  // control motor A spins clockwise
  //digitalWrite(IN2_PIN, LOW);   // control motor A spins clockwise 

  
}

void loop() {
   
int var;
   var = 0;
   while (var <5) {
  //for ( int i = 0; i < 4; i++) {

    int Sp1 = (random(5,9)*25); // random speed 1
    int Sp2 = (random(5,9)*25); // random speed 2

 

    limitSwitch_1.loop();  // MUST call the loop() function first
    limitSwitch_2.loop();  // MUST call the loop() function first

      if (limitSwitch_1.isPressed()) {
        direction *= -1;  // change direction
        Serial.println(F("The limit switch 1: TOUCHED"));
      }

      if (limitSwitch_2.isPressed()) {
        direction *= -1;  // change direction
        Serial.println(F("The limit switch 2: TOUCHED"));
      }
      
      if (prev_direction != direction) {
        Serial.print(F("The direction -> "));
        if (direction == DIRECTION_CW) {
          Serial.println(F("CLOCKWISE"));
          digitalWrite(IN1_PIN, HIGH);  // control motor A spins clockwise
          analogWrite(ENA_PIN, Sp1);
          //digitalWrite(IN2_PIN, LOW);   // control motor A spins clockwise
        } else {
          Serial.println(F("ANTI-CLOCKWISE"));
          digitalWrite(IN1_PIN, LOW);   // control motor A spins anti-clockwise
          analogWrite(ENA_PIN, Sp2);
          //digitalWrite(IN2_PIN, HIGH);  // control motor A spins anti-clockwise
      }
      
      prev_direction = direction;

    
    }
  //}
  var++;
   }
} 

What exactly happens when you run the sketch ? What do you see in the Serial monitor ?

I have not looked at your code in detail but I notice that you do not call the button.loop() function in your while loop. I am not familiar with the button library that you are using but I suspect that without a call to that function then the buttons will not be read

move this outside of loop(), make it:

int var = 0;

The way your code works right now, it loops 5 times during the first iteration of loop(), then begins the second iteration of loop(), during which it cycles 5 times, then begins the third iteration of loop(), ad infinitum et nauseam.

With the declaration outside of loop(), it starts at zero, increments 5 times, and never resets, so it only executes the content 5 times.

It’s not really the best way to do it, but it illustrates how close you came to getting it ‘right’, wrongly. I’ll post a revision in a minute to show you how it could be done.

Oh, and look into what Bob said.

#include <ezButton.h>


#define DIRECTION_CCW -1
#define DIRECTION_CW 1

#define ENA_PIN 3  // The Arduino pin connected to the EN1 pin L298N
#define IN1_PIN 12  // The Arduino pin connected to the IN1 pin L298N
//#define IN2_PIN 9  // The Arduino pin connected to the IN2 pin L298N

//define relay pin here

ezButton limitSwitch_1(4);  // create ezButton object that attach to pin 4
ezButton limitSwitch_2(5);  // create ezButton object that attach to pin 5
ezButton button(7);  // create ezButton object that attach to pin 7;

int direction = DIRECTION_CW;
int prev_direction = DIRECTION_CW;

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

  limitSwitch_1.setDebounceTime(50);  // set debounce time to 50 milliseconds
  limitSwitch_2.setDebounceTime(50);  // set debounce time to 50 milliseconds
  button.setDebounceTime(50); // set debounce time to 50 milliseconds

   int seed = analogRead(0);

 randomSeed(seed);

 
 while (!button.isPressed())
   button.loop(); // MUST call the loop() function first 
 
  // initialize digital pins as outputs.
  pinMode(ENA_PIN, OUTPUT);
  pinMode(IN1_PIN, OUTPUT);
  //pinMode(IN2_PIN, OUTPUT);
 //insert relay command here
  delay(5000);

  analogWrite(ENA_PIN, 250);  // max speed
  digitalWrite(IN1_PIN, HIGH);  // control motor A spins clockwise
  //digitalWrite(IN2_PIN, LOW);   // control motor A spins clockwise 

  
}

int var = 0;
   
void loop() {
   

   if(var <5) {
  //for ( int i = 0; i < 4; i++) {

    int Sp1 = (random(5,9)*25); // random speed 1
    int Sp2 = (random(5,9)*25); // random speed 2

 

    limitSwitch_1.loop();  // MUST call the loop() function first
    limitSwitch_2.loop();  // MUST call the loop() function first

      if (limitSwitch_1.isPressed()) {
        direction *= -1;  // change direction
        Serial.println(F("The limit switch 1: TOUCHED"));
      }

      if (limitSwitch_2.isPressed()) {
        direction *= -1;  // change direction
        Serial.println(F("The limit switch 2: TOUCHED"));
      }
      
      if (prev_direction != direction) {
        Serial.print(F("The direction -> "));
        if (direction == DIRECTION_CW) {
          Serial.println(F("CLOCKWISE"));
          digitalWrite(IN1_PIN, HIGH);  // control motor A spins clockwise
          analogWrite(ENA_PIN, Sp1);
          //digitalWrite(IN2_PIN, LOW);   // control motor A spins clockwise
        } else {
          Serial.println(F("ANTI-CLOCKWISE"));
          digitalWrite(IN1_PIN, LOW);   // control motor A spins anti-clockwise
          analogWrite(ENA_PIN, Sp2);
          //digitalWrite(IN2_PIN, HIGH);  // control motor A spins anti-clockwise
      }
      
      prev_direction = direction;

    
    }
  //}
  var++;
   }
}

There. That takes advantage of the looping of loop(). To cause the action to repeat, all you need to do is reset var to 0 when “start” is pressed, and loop will happily execute the code another 5 times. More complexity, I will leave up to you to define.

look this over. i bleieve it is easier to understand.

no need to debounce buttons or limit switches. assume switches wired between the pin and ground.
dircw breaks the condition once the limit switch becomes active.
not sure about CW and CCW limit switches

const byte ENA_PIN =  3;  // L298N pin
const byte IN1_PIN = 12;  // L298N pin
const byte IN2_PIN =  9;  // L298N pin

const byte PinBut    = 7;
const byte PinLmtCcw = 4;
const byte PinLmtCw  = 5;

const int Max = 5;
int       cnt;
bool      dirCw;

// -----------------------------------------------------------------------------
void loop ()
{
    if (Max <= cnt)
        return;

    // check if reached CCW limit switch
    if (! dirCw && LOW == digitalRead (PinLmtCcw))  {
        dirCw = true;
        Serial.println ("CLOCKWISE");
        digitalWrite (IN1_PIN, HIGH);       // CW
        analogWrite  (ENA_PIN, random (5,9) *25);
        cnt++;
    }

    // check if reached CW limit switch
    else if (dirCw && LOW == digitalRead (PinLmtCw))  {
        dirCw = false;
        Serial.println ("Counter-Clockwise");
        digitalWrite (IN1_PIN, LOW);        // CCW
        analogWrite  (ENA_PIN, random (5,9) *25);
        cnt++;
    }

    // done
    if (Max == cnt)
        Serial.println ("Stop");
}


// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);
    Serial.println ("ready");

    pinMode (ENA_PIN, OUTPUT);
    pinMode (IN1_PIN, OUTPUT);
    pinMode (IN2_PIN, OUTPUT);

    pinMode (PinBut,    INPUT_PULLUP);
    pinMode (PinLmtCcw, INPUT_PULLUP);
    pinMode (PinLmtCw,  INPUT_PULLUP);

    randomSeed (analogRead (0));

    // wait to start motor
    while (HIGH == digitalRead (PinBut))
        ;
    Serial.println (" triggered");

    delay (5000);

    analogWrite  (ENA_PIN, 250);  // max speed
    digitalWrite (IN1_PIN, HIGH);  // control motor A spins clockwise
    digitalWrite (IN2_PIN, LOW);   // control motor A spins clockwise

    dirCw = true;

    Serial.println ("  run");
}

@UKHeliBob raises a good point about ezButton, but it does look like you have called its service method .loop() correctly, as button is only used in setup.

It probably works, but the comment suggests one tweak

 button.loop();  // first, amIRight?
 while (!button.isPressed())
   button.loop(); // MUST call the loop() function first  

One thing you might take from @gcjr's sketch is the simplified manner of waiting on that button in setup(), viz:

But use a different name to PinBut, please. :expressionless:

And turn on compiler warnings in the IDE and continue to write your conditionals normally. To a first approximation, no one uses Yodel notation, and it just makes regular people work harder to read your code.

// up top

const byte startButton = 7:
auto const PRESSED = LOW;
auto const NOTPRESSED = HIGH;


// later
    // wait to start motor
    while (digitalRead(startButton) != PRESSED)
        ;

@gcjr also points out that debouncing is unnecessary, but it is not a bad habit to know you need to have in most cases.

@camsysca's code exploits the looping nature of loop(), which you def wanna get your head around, it will make everything else you may want to do with this code easier.

I'm with Uncle Bob on debuggers. Just now, it is a waste of your time, no matter you've exploited them to good effect elsewhere. Your best and first friend in this world is serial printing: make your sketch talk, and check the values of key variabkes, and whether they properly inform the flow through your code. It look ps like you are already doing, so… does the serial output give you confidence?

I have never used the or a debugger in my Arduino play. I do have a logic analyser, it's a very rare occasion it gets dragged in.

A big problem with debugging is that things don't run in real time. In the embedded world, that can cause more problems than there were to find in the first place.

So the best is to keep bugs from ever being in the code, which just means careful step by step accretion of code, with verification that it is doing what you want, in the way you wrote it, at each step. Always aim to have a living sketch that works, even if it doesn't yet slice the bread,


   var = 0;
   while (var <5) {
  //for ( int i = 0; i < 4; i++) {

Lastly, maybe, tinkering with for versus while is OK, but should only increase your confidence that they both work exactly like the book any book says. If one expression of initialise, compute, increment (or whatever) and test does not make the compute part, um, compute, neither will any other way of expressing it.

In your case, both worked and both failed. It is somewhat easier to see the whike loop fail, and while loops make the logic manifest, which makes it easier to see how to convert it to Arduino looping loop style. Hence for loops show up more for doing something that takes little time, and while has built right into its name the elides that time might be spent. So think while, but exploit the loop() you get out of the box.

Finally, small suggestion. You've randomized random, I like to leave that for last: every test will run through the same sequence, which in the case of, say, an annoying game of logic is an advantage. You can focus on why something doesn't do exactly what you know it should. Throw in the random seed thing later.

a7

Thank you all for you input, it is much appreciated and, amazingly, I actually understand your guidance. I had the impression that I had to have it all in setup or all in loop, both of which I tried (and failed :frowning:) I figured out that the sketch was looping in the loop and now I think I've got how break the unwanted looping.
I started the sketch of carrying out the most basic functions and, as I got the code to work, added the extra functions, as you suggested ( engineer here!)
I'm off to make some changes now and see what else I can break! Thanks again

Hello

for your info, the main function (defined in main.cpp in ~/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino on my computer) looks like this:

int main(void)
{
	init();

	initVariant();

#if defined(USBCON)
	USBDevice.attach();
#endif
	
	setup();
    
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}
        
	return 0;
}

it is started on boot/reset of arduino and is responsible for running setup() first and then loop() in neverending loop. (serialEventRun is just internal Serial managing )

if you want something happen just 5 times (for example), you may do it in setup() and leave loop() empty to do nothing after you finished, like this

void setup(){
  Serial.begin(9600); // I use 115200, as it is way faster, need to set it also on PC
  Serial.println("Program started");
  ... // all other stuff
  Serial.println("All is set up");
  for(int i=0; i<5; i++) {
    Serial.print(“Entering loop #”); Serial.println(i);
    // this will run 5 times
    //
    int Sp1 = (random(5,9)*25); // random speed 1
    int Sp2 = (random(5,9)*25); // random speed 2
    // or other stuff
    Serial.print("End of loop #"); Serial.println(i);
  }; // end of for
  // 
  Serial.println("The END!!!");
  Serial.println("empty loop(){} follows");
}
void loop() {} // all finished, the code will cycle here forever / until reset

or you may have something for the loop to do, after the main part (repeat someting 5x) is done. Like showing adds, blink LEDS, or anything else.

At the begining Serial.println(“I am at this part”); or Serial.print(“value of Sp1 =”);Serial.println(Sp1); may help a lot and cost nearly nothing.

It really helps on debugging (namely to notice, that arduino restarts, or loop is looping more, than expected (or not at all) or other strange/common problems)

And when it works, it is easy to find Serial.print and delete/comment it out.

On the other hand I often use disassembler to see, what code was generated from my source and simulator to step it through

Disassembler for ATmega2560 (Arduino Mega R3)

I usuall start it, set breakpoints at some interesting places (like main (b + main), setup (b + setup), loop, this and that functions), then run (r) it to some interesting area, step in (s) or step over (n) called functions and code, place more breakpoints to interesting addresses (b + 0x095c (some place inside function) and see, what happens in memory dump (d 0x200 ? 0x600 )

With strategically placed breakpoints it is easy to see anything (and by manipulating memory is easy set input keys or anything else at right time too).

It is more hardocore, then is usual for beginers here, but it is also extremly powerfull. So it may be of some use for you :slight_smile:

Thank you for taking the time to point me in the right direction so I can solve my problem. With your advice and that from others, I have a clear picture of what to do now. I am going to take a few days away from this and then put my loops in the right place.