while(run) loop in arduino's void loop() exiting then coming back to it

Hi

I have a DoThings() function in the Arduino loop that doesn’t behave as attended :

void loop() {
DoThings();
DoNextThingsOnlyWhenReturningFromTheDoThingsFunction();
}

//Function definition must do something and only that thing for a certain time !
void DoThings(int const &timer){
bool run = 1;
static unsigned long timer_end = millis() + timer;
while (run){
now = millis();
do different things(); // And absolutely only that while run=1!!!
* 
*
*

run = (now < timer_end)
}
}

it seems do get out of the while(run) at each iteration even if “(now < timer_end) = true” so it’s doing other things following in the arduino’s loop() and come back later to my while(run) function !
But I don’t want these other things to be done before ending the while(run) loop.

Is it the special way the arduino’s loop works that gives me this strange behaviour ?
And how to get the effect I want ?

Your code, even as a snippet, is so far from being able to pass successfully through a compiler that it was obviously typed from your memory. Which means it is a waste of time for anyone on this side of your monitor to even glance at it.

static unsigned long timer_end = millis() + timer;

Addition with millis is a bad choice.

That doesn't compile. You declared DoThings(int...) so it takes one parameter but then you call it with no parameter.

Did you declare another DoThings() somewhere else?

Here is the entire code.

ARDUINO’s LOOP ! ############ shouldn’t appear before the while(run) loop finish !
#include <Wire.h> //  pour l'I2C avec SDA sur pin A4 et scl sur A5
#include <Adafruit_MCP4725.h> //  I2C device
#include <SPI.h>  //  pour le SPI
#include "Adafruit_MAX31855.h"  //  SPI device
#include <Adafruit_ADS1015.h>
#include <PID_v1.h> //  bibliothèque régulateur PID

// Example creating a thermocouple instance with software
//SPI on any three digital IO pins.
#define MAXDO   12
#define MAXCS   2
#define MAXCLK  3
// initialize the Thermocouple
Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO);

enum sortie_capteur {Tc_K_ON, Pyro_ON};
sortie_capteur capteur_en_service = Tc_K_ON;
String capteur[2] = {"Tc_K_ON", "Pyro_ON"};

struct Temp {
  double Tc_K;
  double internal_C;
  double T_pyro;
  double feedback;
  sortie_capteur flag_pyro;
};

//  ###Déclaration des variables###
bool DEBUG = true;
int taux_rafraichissement_Serial_monitor = 750; // période de rafraichissement du moniteur série
unsigned long last_update_monitor(0);
unsigned long last_read_temp(0);
int taux_rafraichissement_output (200);   // période de rafraichissement sortie commande
unsigned long last_update_MCP4725 = 0;
double c = 0;
double internal_C = 0;
Temp temperatures;  //  struct Temp{double Tc_K; double internal_C; double T_pyro; double feedback; sortie_capteur flag_pyro; };

//  ####### arguments à demander avant de lancer le programme #############
int rampe = 5; //  °C/min
int temp_palier = 30; //  °C
unsigned int duree_palier = 1; //  duree palier en minute

//  ################# PID ###################
//Define Variables we'll be connecting to
double Consigne = 0.0;
double feedback = temperatures.feedback;
double output = 666.0;
//Specify the links and initial tuning parameters
double Kp = 2;
double Ki = 0;
const double Kd = 0;
//char[] POn = P_ON_E);  //  pour les paliers
char* POn = P_ON_M; //   Proportional on Measurement (pour les rampes)
PID myPID(&feedback, &output, &Consigne, Kp, Ki, Kd, POn, DIRECT);

//  ##############################################

Adafruit_MCP4725 dac;   //avec SDA sur pin A4 et scl sur A5
Adafruit_ADS1115 ads;


//  ############" déclaration des fonctions #################
Temp readTemp(const unsigned long &now, const unsigned int &timer);  //  lit les température ambiante et TC_K (module MAS18855)
void updateMonitor(unsigned long const &now, const double &internal_C, const double &c, const double &Consigne,
                   const double &feedback, sortie_capteur const &flag_pyro, const double &output); // ####### Rafraichit l'affichage moniteur #######
void updateMCP4725(bool Output_to_update, unsigned long now);

void DoAllWhatNeeded(const unsigned long &now);
void DoGradient(unsigned long const &now, double const &temp_amb, unsigned int const &rampe, unsigned int const &temp_palier);



void setup(void) {
  Serial.begin(9600);
  Serial.println("Hello!");

  dac.begin(0x60); // adresse 96 (0x60)

  ads.setGain(GAIN_ONE);        // 1x gain   +/- 4.096V  1 bit = 2mV      0.125mV
  ads.begin();

  //PID initialize the variables we're linked to
  feedback = thermocouple.readInternal();
  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  //  SetOutputLimits(0, 4095);
  readTemp(1, 0);  //  lit une première fois immédiatement les températures
  if (DEBUG)Serial.println("readTemp init");
}

void loop(void) {
  unsigned long now = millis();
  DoGradient(now, temperatures.feedback, rampe, temp_palier);
  Serial.println (" #####   ARDUINO's LOOP !   ############     ");
}


//  ##########  définition des fonctions    #######################
Temp readTemp(const unsigned long &now, const unsigned int &timer) { //  renvoie un strud=ct Temp !
  if (now - last_read_temp > timer) {
    //for(int i=0; i<1; i++){
    int16_t adc0; //, adc1, adc2, adc3;
    adc0 = ads.readADC_SingleEnded(0);
    temperatures.Tc_K = thermocouple.readCelsius();
    temperatures.internal_C = thermocouple.readInternal();
    // ####### calcul la pente et l'ordonnée a l'origine de la fonction de la sortie du pyro en fonction de 2 points
    typedef double Point[2];
    Point A = {250, 0.8}, B{1200, 4};
    static double m = (B[1] - A[1]) / (B[0] - A[0]);
    static double p = A[1] - 4 * m;
    double const ads_step = 0.125;  //  mV/bit
    //  2 points A(250; 1) et B(1250; 5) y= a*adc0 + b  a=(5-1)/(1250-250), a=1/250 1=250a+b, b=1-4a, b=1-4*1/250=0.984
    temperatures.T_pyro = adc0 * ads_step * m + p; //250°C=>4mA=>0.8V, 1200°C=>20mA=>4V (résistance de Shunt 200 Ohm pour 4Vmax@20mA
    if (temperatures.T_pyro > 253) {
      temperatures.feedback = temperatures.T_pyro;
      temperatures.flag_pyro = Pyro_ON;
    } else {
      //temperatures.feedback = temperatures.Tc_K;
      temperatures.feedback = thermocouple.readCelsius();
      temperatures.flag_pyro = Tc_K_ON;
    }
    last_read_temp = now;
  }
  Serial.println("readTemp");
  return temperatures;
}

// ####### Rafraichit l'affichage moniteur #######
void updateMonitor(unsigned long const &now, const double &internal_C, const double &c, const double &Consigne,
                   const double &feedback, sortie_capteur const &flag_pyro, const double &output) { // ####### Rafraichit l'affichage moniteur #######
  //unsigned long now(millis());
  int timer = now - last_update_monitor;
  if (timer > taux_rafraichissement_Serial_monitor) {
    if (isnan(c)) {
      Serial.println("");
      Serial.println(F("Something wrong with thermocouple!"));
      //Serial.println("Something wrong with thermocouple!#######################################################");
    } else {
      Serial.print(now / 1000);
      Serial.print("\t");
      Serial.print(timer);
      Serial.print("\t");
      Serial.print(internal_C);
      Serial.print("\t");
      Serial.print(c);
      Serial.print("\t");
      Serial.print(Consigne);
      Serial.print("\t");
      Serial.print(feedback);
      Serial.print("\t");
      Serial.print(capteur[flag_pyro]);
      Serial.print("\t");
      Serial.println(output); // 0<output<254 ### 0<MCP4725<4095
      last_update_monitor = now; //  pour le serial monitor
    }
  }
}
void updateMCP4725(bool Output_to_update, unsigned long now) {
  if (!Output_to_update) {
    Serial.println("No Output_to_update");
    return;
  } else {
    Serial.println("updateMCP4725 Output_to_update");
    int timer = now - last_update_MCP4725;
    if (timer > taux_rafraichissement_output) {
      dac.setVoltage(output, false);
      last_update_MCP4725 = now;
      Serial.println("updateMCP4725 Updated Output");
    }
  }
}

void DoGradient(unsigned long &now, double const &temp_amb, unsigned int const &rampe, unsigned int const &temp_palier) {
  bool run = 1;
  static double rampe_ms = rampe / (60.0 * 1000); // rampe en °C/ms au lieu de °C/min
  Serial.print("rampe_ms : ");
  Serial.print(rampe_ms);
  static unsigned long duree_rampe = (temp_palier - temp_amb) / rampe_ms; //  en ms
  Serial.print("    duree_rampe : ");
  Serial.print(duree_rampe);
  static unsigned long timer_end = now + duree_rampe;  //  en ms
  Serial.print("    timer_end_rampe : ");
  Serial.println(timer_end);
  while (run == 1) {
    now = millis(); //  actualise le temps pour le reste du programme

    Consigne = temp_palier - (timer_end - now) * rampe_ms;
    DoAllWhatNeeded(now);
    Serial.print("DoGradient; now = ");
    Serial.print(now);
    Serial.print("    remaning_time gradient(ms) :  ");
    long  remaning_time = timer_end - now;
    if (remaning_time > 0) {
      Serial.println(remaning_time);
    } else {
      Serial.println("Timer over gradient!");
      run = 0;
    }
    run = (now <= timer_end);
    Serial.print("RUN gradient= ");
    Serial.println(run);
    if (run = false) {
      Serial.println("DoGradient endded");
    }
  }
}

void DoAllWhatNeeded(const unsigned long &now) {
  readTemp(now, 200);  //  lit les température ambiante, TC_K (module MAX18855) et pyromètre et switch feedback sur le capteur adéquate
  //feedback = thermocouple.readInternal();
  updateMCP4725(myPID.Compute(), now); // myPID.Compute() return True if something has changed, False if nothing has been done
  //updateMonitor(internal_C, c, Consigne, feedback, temperatures.flag_pyro, output);  // ####### Rafraichit l'affichage moniteur #######
  updateMonitor(now, temperatures.internal_C, temperatures.Tc_K, Consigne, temperatures.feedback, temperatures.flag_pyro, output);  // ####### Rafraichit l'affichage moniteur #######

  Serial.println("DoAllWhatNeeded");

}
bool run = 1;

...
while (run == 1) {

Boolean variables can be either true or false. While internally the compiler is allowed to use 1 and 0 to store these values it is poor practice to rely on this. Don't compare booleans with integers.

Why not just write while(run) {?

Read Nick Gammon's excellent post on millis to find out why adding milliseconds is bad.

Now to the real problem....

if (run = false) {

Perhaps you meant to use == ? Or better, use the ! operator.

if(!run) {

It is not a good idea to use the String (capital S) class on an Arduino as it can cause memory corruption in the small memory on an Arduino. Just use cstrings - char arrays terminated with 0.

Have a look at how millis() is used to manage timing without blocking in Several Things at a Time.

And see Using millis() for timing. A beginners guide if you need more explanation.

IMHO it is much better to use loop() to do any repetition that lasts for more than a number of microseconds. Just use IF in place of a long-running WHILE or FOR.

...R