Code doesn't execute properly for some reason

Hello, I'm having issues with this code. I'm trying to trigger a laser for 1 second, then shut off the laser for 1 second, and turn it back on again. And I want to do this for a total 'session time' of 1 minute as you will see in the code. The code is triggered to start by HIGH on a terminal that's sensing a feeder which turns on. Using the millis() function, I have a while loop inside a while loop and for some reason this is causing issues. However, the issue is that, the code just runs through (doesn't even wait for the feeder to turn on HIGH) and when I print the total session time (at the end, as you will see), it says the code ran for 1999 milliseconds instead of the full minute as it is supposed to. But when I delete the while loop inside the outer while loop, the code functions again.

This is the code:


int switchState_Feeder = 0;
int switchState_Pellet = 0;


long sessiontime;
long sessiontimestart;
long sessiontimestop;
long totalsessiontime; 

long currentMillis = 0;


long Millis_start = 0;
long Millis_stop = 0;



void setup() {
  
  // put your setup code here, to run once:
  
  

  pinMode(10,INPUT); //Feeder input
  pinMode(6,INPUT); //Pellet input
  pinMode(3, OUTPUT); //Laser output
  pinMode(2,OUTPUT); //LED output
  digitalWrite(3, LOW);
  sessiontime = 60000;
  
  Serial.begin(9600);
  
  digitalWrite(2, HIGH);
  delay(2000);
  digitalWrite(2, LOW);

  delay(0);
}

void loop() {
  
  switchState_Feeder = digitalRead(10); // First time feeder goes off, that's when session begin
  
  if (switchState_Feeder == HIGH) {
     
           sessiontimestart = millis();
              digitalWrite(2, HIGH);
              
               while (currentMillis - sessiontimestart < sessiontime) {

                 Millis_start = millis();

                    while (Millis_stop - Millis_start <1000){
                       
                       digitalWrite(3, HIGH); // 10hz laser ON  for 1 second according to loop
                       delay(10);
                       digitalWrite(3, LOW);
                       delay(90);
                       
                        Millis_stop = millis();
                    }
                      delay (1000); //shutting off laser for 1 second
                       currentMillis = millis();
                    }
        
               
        }
       digitalWrite(2, LOW);

       sessiontimestop = millis();
       totalsessiontime = sessiontimestop - sessiontimestart;
       
                Serial.print("Total Session Time...");
                Serial.println(totalsessiontime);
                
                Serial.end();
                    
  }

    

Hello nkamatka

Prevent the ussage of the delay() function and while() in conjunction of the millis() function.

To build your own timer use the BLINKWITHOUTDELAY example with some mods needed. This example is the mother of all timers used in the Arduino biotop.

Have a nice day and enjoy coding in C++.

one issue is buttons are typically connected between the pin and ground, the pint configures as INPUT_PULLUP to use the internal pullup resistor which pulls the pin HIGH and when pressed, the button pulls the pin LOW.

when i ran your code, it reported

Total Session Time...60909
Total Session Time...60409

but your nested loops make it a bit difficult to verify the timing. looks like you have a couple modes: one that your session is active (i.e. enabled by a button press) and within it, the laser flashes for a second and then is off for a second. since a session is 60 seconds, this should happen exactly 30 times, but the total timing also include time to process commands

Thank you! This is very strange.
I manipulated a different code that works on my system with this code and it seems to be working again (I'm getting a slightly larger number, but yes that is due to the nested while loops and the amount of time that the program is running for).

Do you think having it open in a different IDE is causing issues? So weird.

since your not interested in non-blocking code, i believe the following is simpler. i got a session time of 10002 msec for 5 laser on/off cycles. i believe you want 30 cycles for a total of 60 seconds

#undef MyHW
#ifdef MyHW
byte PinFeeder = A1;
byte PinPellet = A2;
byte PinLaser  = 13;
byte PinLed    = 12;

enum { Off = HIGH, On = LOW };

#else
byte PinFeeder = 10;
byte PinPellet = 6;
byte PinLaser  = 3;
byte PinLed    = 2;

enum { Off = LOW, On = HIGH };
#endif

// -------------------------------------
#define Nlaser  10
#define Ncyc    5

unsigned long MsecLaserOn    = 10;
unsigned long MsecLaserOff   = 90;
unsigned long MsecLaserPause = 1000;

unsigned long msecStart;;

// -----------------------------------------------------------------------------
void loop ()
{
    // check button
    if (LOW == digitalRead (PinFeeder))  {
        msecStart = millis ();

        digitalWrite (PinLed, On);
        for (int n = 5; n > 0; n--)  {
            for (int i = 10; i > 0; i--)  {
                digitalWrite (PinLaser, On);
                delay (MsecLaserOn);
                digitalWrite (PinLaser, Off);
                delay (MsecLaserOff);
            }
            delay (MsecLaserPause);
        }
        digitalWrite (PinLed, Off);

        Serial.print ("Session Time: ");
        Serial.println (millis() - msecStart);
    }
}

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

    pinMode (PinFeeder, INPUT_PULLUP); //Feeder input
    pinMode (PinPellet, INPUT_PULLUP); //Pellet input
    pinMode (PinLaser,  OUTPUT);       //Laser output
    pinMode (PinLed,    OUTPUT);       //LED output

    digitalWrite (PinLaser, Off);

    digitalWrite (PinLed, On);
    delay (2000);
    digitalWrite (PinLed, Off);
}

Here comes a non-blocking version anyway

I have changed baudrate to 115200 which is the new standard-baudrate

the main thing is to have 4 different modes of operation

const byte sm_Idling        = 0;
const byte sm_Laser10ms_On  = 1;
const byte sm_Laser90ms_Off = 2;
const byte sm_Wait1SecOff   = 3;

idling waiting for

if (switchState_Feeder == HIGH)

then starts the laser pulsing on/off/on/off pause 1 second
repeat this until 60 seconds have passed by

This means void loop is as short as

void loop() {
  LaserPulseStateMachine();
}

because everything is happening inside function LaserPulseStateMachine()

The code has macros for serial debug-output which must be on top of the file to make it work

I gave your IO-pins self-explaining names

This code uses my version of a non-blocking timer-function which I claim is more intuitive as the standard blink without understanding example of the arduino-IDE

non-blocking timing allows that the code is always responsive to user-input as
stopping the pulsing at any time with another button or updating a display
etc.

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *

// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a
// Serial.print is executed
// end of macros dbg and dbgi
// print only once when value has changed
#define dbgc(myFixedText, variableName) \
  do { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  } while (false);
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *


byte switchState_Feeder = 0;
byte switchState_Pellet = 0;

long sessiontime;
long sessiontimestart;
long sessiontimestop;
long totalsessiontime;

unsigned long pulseTimer;
unsigned long mySessionTimer;
unsigned long Active1SecTimer;
unsigned long Pause1SecTimer;

const unsigned long LaserOnTime  = 10;
const unsigned long LaserOffTime = 90;

const byte sm_Idling        = 0;
const byte sm_Laser10ms_On  = 1;
const byte sm_Laser90ms_Off = 2;
const byte sm_Wait1SecOff   = 3;

byte myState = sm_Idling ;

long Millis_start = 0;
long Millis_stop = 0;

const byte Feeder_input_Pin = 10;
const byte Pellet_input_Pin =  6;
const byte Laser_output_Pin =  3;
const byte LED_output_Pin   =  2;

void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

void setup() {

  pinMode(Feeder_input_Pin, INPUT);
  pinMode(Pellet_input_Pin, INPUT);
  pinMode(Laser_output_Pin, OUTPUT);
  pinMode(LED_output_Pin,   OUTPUT);
  digitalWrite(Laser_output_Pin, LOW);
  sessiontime = 60000;

  Serial.begin(115200);
  PrintFileNameDateTime();

  digitalWrite(LED_output_Pin, HIGH);
  delay(2000);
  digitalWrite(LED_output_Pin, LOW);
}


void LaserPulseStateMachine() {
  dbgc("L:", myState);

  switch (myState) {

    case sm_Idling:
      switchState_Feeder = digitalRead(Feeder_input_Pin);
      dbgi("idling", switchState_Feeder, 1000);
      if (switchState_Feeder == HIGH) {
        //digitalWrite(LED_output_Pin, HIGH);
        mySessionTimer = millis();  // store snapshot of time into timer-variable
        Active1SecTimer = millis(); // store snapshot of time into timer-variable
        sessiontimestart = millis();
        myState = sm_Laser10ms_On;
        pulseTimer = millis();      // store snapshot of time into timer-variable
      }
      break; // immidiately jump down to End-of-Switch

    case sm_Laser10ms_On:
      digitalWrite(Laser_output_Pin, HIGH); // 10hz laser ON  for 1 second according to loop
      if ( TimePeriodIsOver(pulseTimer, LaserOnTime) ) {
        pulseTimer = millis(); // store snapshot of time into timer-variable
        dbg("jump to Off", 0);
        myState = sm_Laser90ms_Off;
      }

      // check if 60 seconds have passed by
      if (TimePeriodIsOver(mySessionTimer, sessiontime) ) {
        digitalWrite(Laser_output_Pin, LOW);
        sessiontimestop = millis();
        totalsessiontime = sessiontimestop - sessiontimestart;

        Serial.print("Total Session Time...");
        Serial.println(totalsessiontime);

        myState = sm_Idling;
        break; // immidiately jump down to End-of-Switch
      }
      break; // immidiately jump down to End-of-Switch

    case sm_Laser90ms_Off:
      digitalWrite(Laser_output_Pin, LOW);
      if ( TimePeriodIsOver(pulseTimer, LaserOffTime) ) {
        pulseTimer = millis(); // store snapshot of time into timer-variable
        dbg("jump to On", 0);
        myState = sm_Laser10ms_On;
      }

      // check if one second has passed by
      if ( TimePeriodIsOver(Active1SecTimer, 1000) ) {
        Pause1SecTimer = millis(); // store snapshot of time into timer-variable
        dbg("90ms off jump to pause ", 0);
        myState = sm_Wait1SecOff;
      }
      break; // immidiately jump down to End-of-Switch

    case sm_Wait1SecOff:
      digitalWrite(Laser_output_Pin, LOW);
      // check if one second has passed by
      if ( TimePeriodIsOver(Pause1SecTimer, 1000) ) {
        Active1SecTimer = millis();
        dbg("jump to active 10ms on", 0);
        myState = sm_Laser10ms_On;
        pulseTimer = millis(); 
      }
      break; // immidiately jump down to End-of-Switch

  } // End-of-Switch
}

void loop() {
  LaserPulseStateMachine();
}

best regards Stefan

Wow, thanks a lot! I'll try this to see if it works.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.