While Loop and Timer Advice

Hello fellow friends,

Been awhile since I last posted since I was working on my diesel glow plug controller project. I want to say I finally got the engine installed and running and have been able to test out my glow plug controller in a live environment and it works as designed!

I did have some difficulty with a portion of my code and my thread asking for advice went a little sideways and I got into a little trouble. I ultimately was able to fix the offending code and create a compromise. It in fact works but I would like to optimize the code to work as I originally had desired.

Alright lets get into it....In this portion of code I am triggering a relay based on engine coolant temperature and a 12v signal from the vehicle alternator. The 12v signals that the engine has started.

The desired behavior would be to read the coolant temp sensor and 'while' it is greater then 1.23v (less than 140F) it will trigger the relay (pin HIGH). I would like to place 2 conditions on this trigger, a) Temperature less then 140F (>1.23v) OR b) Timer of 2min (120000ms) has elapsed.

I previously tried the 'millis' function and have already banged my head against the wall with it and was never successful at making it work, so a simple timer function would be superb, start a timer, poll the time for elapsed time, that's it.
I am using a samd21 chipset and I believe that has a built in timer function but calling on silicone for timing sounds overly complex.

Now I have the code broke into 3 sections, preglow, start glow and afterglow. Everything works as designed including a fail safe mode I coded in the event the coolant sensor became disconnected or failed out of range. The only code I am concerned with right now is the afterglow portion.

My apologies if this is very verbose, but I feel that if you have all the background then it will be easier to assist in my request for help.

The following code works as written, if volts higher than 1.23v it will hold the relay on for 5 seconds and go low, and if less then 1.23v it will not trigger the relay.

Existing 'afterglow' code:


  // Alternator signal detected, engine has started
  if ((!afterglowComplete) && (digitalRead(alternatorPin)) == HIGH) {

    float voltage = calculateVoltage(analogRead(temperaturePin)); // Read temp sensor
    if (voltage > 1.23) { // Check if temperature less the 140*F
      delay(250);
      digitalWrite(relayControlPin, HIGH); // If temp less then 140*F, turn relay on for xx-sec
      delay(5000);
      digitalWrite(relayControlPin, LOW);
      afterglowComplete = true; // Exit after-glow mode

    }
  }

  //-------------------------------------------------------------------------------

So what I would like to do is use a very simple timer function to just start a simple countdown and just be able to poll that count down. So perhaps something like this.....

// Alternator signal detected, engine has started
  if ((!afterglowComplete) && (digitalRead(alternatorPin)) == HIGH) {

// Start a timer ( I am guessing at this since I don't know)
    int timer = elapsedtime(start(timer)); 
     
    float voltage = calculateVoltage(analogRead(temperaturePin)); // Read temp sensor
// Check if temperature less the 140*F OR elapsed time less then 2 minutes
   while (voltage > 1.23 || elapsedtime < 120000) { 

// This should only iterate once while condition is true, should not be iterating over and over cycling relay on and off each time through the loop
      delay(250);
      digitalWrite(relayControlPin, HIGH); 
    }
     
// Once either condition is met, relay goes low and turns off
   if (voltage <= 1.23 || elapsedtime >= 120000) {
      digitalWrite(relayControlPin, LOW);
      afterglowComplete = true; // Exit after-glow mode

    }
  }

  //-------------------------------------------------------------------------------

Hopefully that is clear enough, I really appreciate the feedback. Really would like to figure out a simple timer function. I have seen multiple timer functions written for Arduino code, as well as the built in C++ chrono function. I am just unsure what is going to be the simplest to implement and trouble free.

Thank you all for your assistance.

You may refer to this forum.

It has the code for it.

I appreciate the quick reply, but the problem lies in the fact that the code you refer to is in regard to a timer that starts as soon as the board is powered. This can be extremely variable in this case, the operator could turn on the key and the initial portion of the code could run its course, then he is talking to the wife in the front seat or barking at the kids in the back seat, or fiddling with his cell phone before he actually starts the vehicle. It may not start and he has to turn the key off and back on again re-powering the control unit.

2 minutes or more could have elapsed from when the operator turned the key on powering up the unit. This is like the millis function that I was unable to successfully make work correctly.

I need specifically for a timer to start once the program goes into the 'afterglow' portion of the code. Also this code does not run in a 'loop', it executes each portion of the code exactly once. It will not rerun the code until the unit is powered off and back on again, this is by design. Once the engine is running and up to temperature the control modules job is done. Matter of fact if I could program in a power off function I would do so.

I will keep looking, thanks

Okay. I understand your situation.
If you don't want it when boards start then start the timer, you can see this forum I found online.Click here for the forum
It is similar to your situation. Hope it helps!
Thanks!

Hello ramjeepee

I have read your system description. The frequent use of the delay() function constantly blocks the required real-time processing of the programme.

My recommendation:
A timer function based on the millis() function is actually always required in every project.
The timer function can be easily derived from the BLINKWITHOUTDELAY example, the mother of all Arduino timers, in the IDE.
What does the timer function do?
The timer function provides time slots in which actions are executed without blocking the programme as with the delay() function.
The timer function should be scalable and offer the following services:

  1. make()
  2. start()
  3. stop()
  4. isExpired()
  5. isRunning()

Try it out and programme your own timer function first.

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

p.s. Arrays and structs are your friends.

1 Like

It seems that you do not understand how to use millis() function. The fact that the millis() starts when board is powered do not contradict your task to start timer some seconds/minutes later. Using millis(), you don't need to restart it to start the timer - you just remember a millis() counter value at the moment. To check that the timer has expired - you test the difference of the millis values between start and actual moment.

If you re-read this sentence from user @ramjeepee

IMHO your "explanation" how to code it is 200 lines of text too short.

very good that you explained it in this detailed way.

Still there are some "details" missing.

Post your complete sketch.
From the very first to the very last line of code.

There is a coding technique that combines the
while (some condition)-functionality
with
checking temperature etc.
all the time

This technique is called state-machine

It will take some time to understand it. But once you have understood the principle you never want to go back
The reasons are
very easy to maintain:

  • very easy to expand / reduce
  • reduces the number of if-conditions to the required minimum
    = no hassling with additional flags (if flag set ) or if flag not set etc.

and as you are asking for a "simple" timer-function to check if the timer has expired. They do exist but using any kind of this timer-function requires some code-restructering

I can explain this in more detail adapted to your code if you have posted your complete sketch

best regards Stefan

this sounds like you want to program a Finite State Machine

PREGLOW, STARTGLOW, AFTERGLOW are your states (there might be more...)

Define what should happen in each state,
Define on which event/or after which time you want to go from one state to another.

If I were your, I would draw a state diagram, so everyone can understand what's needed.
Afterwards the programming of a state machine gets easy.

1 Like

what about something like this

const byte alternatorPin   = A2;
const byte temperaturePin  = A0;
const byte relayControlPin = LED_BUILTIN;

const unsigned long TwoMinutes = 3000;      // 2L * 60 * 1000;
      unsigned long msec0;

const int Off       = 0;
const int PreGlow   = 1;
const int AfterGlow = 2;

int state = Off;

float voltage;

// -----------------------------------------------------------------------------
const int   MaxAdc        = 1023;
const float AdcRefVoltage = 5.0;

float
calculateVoltage (
    int   adcValue )
{
    return AdcRefVoltage * adcValue / MaxAdc;
}

// -----------------------------------------------------------------------------
void loop ()
{
    unsigned long msec = millis ();

    switch (state) {
    case Off:
        // Alternator signal detected, engine has started
        if (digitalRead(alternatorPin) == HIGH)  {
            digitalWrite (relayControlPin, HIGH);
            msec0 = msec;
            state = PreGlow;
            Serial.println (" -> PreGlow");
        }
        break;

    case PreGlow:
        // Check if temperature less the 140*F or 2 minutes have expired
        voltage = calculateVoltage (analogRead(temperaturePin));
        Serial.println (voltage);

        if (voltage > 1.23 || msec - msec0 > TwoMinutes)  {
            digitalWrite (relayControlPin, LOW);
            state = AfterGlow;
            Serial.println (" -> AfterGlow");
        }

        if (digitalRead(alternatorPin) == LOW)  {
            digitalWrite (relayControlPin, LOW);
            state = Off;
            Serial.println (" -> Off");
        }
        break;

    case AfterGlow:
        if (digitalRead(alternatorPin) == LOW)  {
            state = Off;
            Serial.println (" -> Off");
        }
        break;
    }

    delay (250);
}

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

    pinMode (alternatorPin,   INPUT_PULLUP);
    pinMode (temperaturePin,  INPUT_PULLUP);

    pinMode      (relayControlPin, OUTPUT);
    digitalWrite (relayControlPin, LOW);
}

Lot of replies since I posted this up last night. Sounds like I better take another crack at attempting use of the 'millis' function.

I apologize for not posting the entire program as written, I just did not want to add any confusion or distract from the task at hand. I will have to read carefully through what everyone has posted here.

I am posting the full current working code as well as the code for when I attempted the millis function.

Current functioning code:

/*************************************
   Glow Plug Controller Software
   Written by Robert Calhoun
   Contributions Joseph Shumaker
   07/10/2023
 * ***********************************
*/

#include <Arduino.h>

const int temperaturePin = A0;
const int alternatorPin = A1;
const int starterPin = A2;
const int relayControlPin = 6;

bool preglowComplete = false; // Flag to indicate if pre-glow sequence is complete
bool starterglowComplete = false; // Flag to indicate if starter-glow sequence is complete
bool afterglowComplete = false; // Flag to indicate if after-glow sequence is complete

int relayOnTime = 0; // Variable to hold the relay "on" time

float calculateVoltage(int sensorValue) {
  float voltage = sensorValue * (3.3 / 1024.0); // Convert the analog value to voltage
  return voltage;
}


void setup() {
  pinMode(relayControlPin, OUTPUT);
  pinMode(starterPin, INPUT_PULLUP);
  pinMode(alternatorPin, INPUT_PULLUP);
}


void loop() {

  //------------------------------------------------------------------------------

  if (!preglowComplete) {
    float voltage = calculateVoltage(analogRead(temperaturePin));

    // Check if voltage is within valid range for temperature sensor
    if (voltage >= 0.0 && voltage <= 3.2) {
      // Switch case statement based on voltage ranges
      switch (int(voltage * 100)) {
        case 282 ... 320:
          relayOnTime = 24;
          break;
        case 208 ... 281:
          relayOnTime = 16;
          break;
        case 123 ... 207:
          relayOnTime = 10;
          break;
        default:
          relayOnTime = 0;
          break;
      }

      // Activate the relay circuit with the calculated relayOnTime
      delay(500);
      digitalWrite(relayControlPin, HIGH);
      delay(relayOnTime * 1000);
      digitalWrite(relayControlPin, LOW);
      preglowComplete = true;


    } else {
      // Failsafe warning signal, if we lose temp sensor(R2), voltage 3.3v
      for (int i = 0; i < 5; i++) {
        digitalWrite(relayControlPin, HIGH);
        delay(1000);
        digitalWrite(relayControlPin, LOW);
        delay(2000);
      }
      digitalWrite(relayControlPin, HIGH);
      delay(10000);
      digitalWrite(relayControlPin, LOW);
      preglowComplete = true;
    }
  }

  //----------------------------------------------------------------------------

  // Starter signal detected, activate the relay control pin
  if ((!starterglowComplete) && (digitalRead(starterPin)) == HIGH) {
    
    delay(250);
    digitalWrite(relayControlPin, HIGH);

    if (digitalRead(starterPin) == LOW) {
      digitalWrite(relayControlPin, LOW); 
      starterglowComplete = true; // Exit starter-glow mode
    }
  }

  //-----------------------------------------------------------------------------

  // Alternator signal detected, engine has started
  if ((!afterglowComplete) && (digitalRead(alternatorPin)) == HIGH) {

    float voltage = calculateVoltage(analogRead(temperaturePin)); // Read temp sensor
    if (voltage > 1.23) { // Check if temperature less the 140*F
      delay(250);
      digitalWrite(relayControlPin, HIGH); // If temp less then 140*F, turn relay on for xx-sec
      delay(5000);
      digitalWrite(relayControlPin, LOW);
      afterglowComplete = true; // Exit after-glow mode

    }
  }

  //-------------------------------------------------------------------------------

}
// Program Complete

In regards to my previous example trying to use the millis function, I can see I had the code all wrong and out of sequence. I remember posted about this, got super frustrated, trashed my thread and got banned for several weeks! Oh ya, thread vandalism was the reason. Like a little kid who get mad and smashes his own toys so no one can play with them..Anyways...

Assuming using the above correct code, here is what I had originally attempted in terms of the 'millis' function.

Declaring variables before 'setup'

unsigned long afterglowStartTime = 0; // Start time of afterglow mode
const unsigned long afterglowDuration = 180000; // Duration of afterglow mode in milliseconds (3 Min)

This is what I had enclosed within the 'afterglow' portion of the code. Upon looking back I can see the code was all wrong, so I may have had the 'millis' portion correct?

unsigned long afterglowCurrentTime = millis(); // Get the current time
    
    // Check if afterglow mode should continue
    if ((calculateVoltage(analogRead(temperaturePin)) <= 1.23) || (afterglowCurrentTime - afterglowStartTime >= afterglowDuration)) {

You should always post complete sketches!
regardless

  • if tsketch does compile or not compile
  • if the behaviour is as you want it or the behaviour is different than you want it

If you want to emphasize a certain part of the code
post the complete sketch and then as a new code-section post the part

Other users that shall help have to get an overview about your code and the ability to look uo each and every detail

so please post the complete sketch where you tried to use millis()

best regards Stefan

this suggests a minor problem when i believe the overall structure of the code is unnecessarily complicated

1 Like

Alright, I am going to take a pause from this thread, I find myself getting overwhelmed from all the responses. I am obviously not good at the interactions expected here, I feel myself getting flustered and not sure why. So best to give it a rest.

Couple notes:
The code I have as written works as expected and designed.
The device is installed in the vehicle and working.

I am just looking to add a simple timer function to one aspect of the code. I am not looking to rewrite the code, I can simply change the delay time to keep the mosfet gate on longer. I was just thinking I might like to optimize the code but it is already getting complicated.

I also think that folks might be missing a key point, this code does not loop. It powers up and it runs down the code like a ladder and that is it, it never reruns itself, it doesn't loop, it doesn't change states, there is no serial monitor, nothing.

It is a black box that has one task, drive a gate on a mosfet in 3 succeeding conditions and done. There is no Arduino board, it is nothing more than a samd21 chipset on a board with 4 inputs and 4 outputs, its less then the size of a US quarter.

Thanks again for the comments, I do appreciate it.

I have to think about it

I have doubts that your code is running down only one single time and never loops.
You are using multiple boolean flags that are at least an indicator that your code might loop

You are using a switch-case-statement which is an indicator that your code might loop

If your code is really not looping the variables

bool preglowComplete = false; // Flag to indicate if pre-glow sequence is complete
bool starterglowComplete = false; // Flag to indicate if starter-glow sequence is complete
bool afterglowComplete = false; // Flag to indicate if after-glow sequence is complete

are not nescessary because your code starts with
pre-glowing runs top-down the steps of pre-glowing
.
then
pre-glowing has finished anyway
.
glowing runs top-down the steps of glowing
then glowing has finished anyway
.
afterglowing runs top-down the steps of afterglowing
then
afterglowing has finished anyway code could stop.

But I guess your code IS looping
When does your microcontroller gets connected to power?

As soon as the car-battery-clamps are put on the battery-bolts and then the microcontroller is connected to power until you remove the clamps.
Which means even if your car is switched of parking the microcontroller is still powered?
.
or.
.
gets your microcontroller only power if you stick in the ignition-key into the ignition-lock and turn the ignition-key to the first position "power on" ?

If your microcontroller is only switched on by the ignition-key your complete code could be inside function setup that is gueranteed to run down the code only a single time.

best regards Stefan

1 Like

Hello ramjeepee

Don't give up now!

Close the Arduino IDE and sit back and take some time to do some design work before you start programming again.

Below you can see my proposal for a transition state diagram to be optimized to your system design.

Formulate the functionality of the states (IDLE, PREGLOW ....) and the conditions for the transitions, t0 to t3, from one state to another in simple sentences.
These transitions can either be event-controlled or time-controlled. A TimerManager is required for time control.

Once you have formulated all logically and functionally, you can start coding.

The ENUM and SWITCH/STATE instructions are best suited at the beginning for programming a finite state machine, shortly called FSM.

Just start with a simple programme.

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

1 Like

@ramjeepee

Thanks @paulpaulson! A piece of very motivational advice! :slight_smile:
Yes!! You shouldn't give up!!!

Programming at C is fun!

Here's a wokwi simulation with OP's code plus some serial.print instrumentation.

The post #11 code is copied into the README.md/"Description" tab for easy reversion.

The code is somewhat like a state machine, using the !preglowComplete, !starterglowComplete and !afterglowComplete booleans as a set of state variables. With mutiple logical variables you don't get good mutual exclusivity between the states unless the complicated if() logical clauses are perfectly designed. If you have many interacting logical variables, they combine combinatorially into many complicated/nested if() clauses as you feature-creep adds more variables.

The switch-case form of state machine does well at the mutual exclusivity, and can simplify the logical clauses and the transitions, making a program easier to understand, or extend.

please explain on a

what is ENUM?

what is a