Implementing a Temperature Cycle

Hi, I'm controlling a curing oven with an Arduino and want to improve the way the temperature cycle is implemented.

Right now, I have a function with if statements that checks the elapsed minutes and returns a target temperature. It works but I'm looking for a method to conveniently store and call different curing cycles.

I'm relatively new to programming and think there must be a way to store this in an array or struct or something else. Can you push me in the right direction?

A cycle could look like this. Changes in temperature should be linear.

minutes | temperature °C
   0    |     20
  30    |     70
 120    |     70
 150    |    120
 230    |    120
 260    |     20

If you would post your current code it would help us understand what you are describing better and formulate a solution.

Perhaps you want to make a step function like this?

struct Stage {
  uint16_t time;
  uint16_t temperature;
};

uint16_t target(uint16_t const time) {
  Stage const stages[] = {{0, 20}, {30, 70}, {120, 70}, {150, 120}, {230, 120}, {260, 20}};

  uint16_t temperature = stages[0].temperature;
  for (Stage const& stage: stages) {
    if (stage.time > time) {
      return temperature;
    }
    temperature = stage.temperature;
  }
  return temperature;
}

For example, the following

for (size_t t = 0; t < 300; t += 10) {
  Serial.print(t);
  Serial.print(' ');
  Serial.println(target(t));
}

results in:

0 20
10 20
20 20
30 70
40 70
50 70
60 70
70 70
80 70
90 70
100 70
110 70
120 70
130 70
140 70
150 120
160 120
170 120
180 120
190 120
200 120
210 120
220 120
230 120
240 120
250 120
260 20
270 20
280 20
290 20

[edit]

Alternatively, if you have more than one of these cycles, you may want to consider the following:

template <size_t n> uint16_t target(Stage const (&stages)[n], uint16_t const time) {
  size_t i;
  for (i = 0; i < n and stages[i].time <= time; i++);
  return stages[i - 1].temperature;
}

which can be used as follows:

Stage const stages[] = {{0, 20}, {30, 70}, {120, 70}, {150, 120}, {230, 120}, {260, 20}};
for (size_t t = 0; t < 300; t += 10) {
  Serial.print(t);
  Serial.print(' ');
  Serial.println(target(stages, t));
}
1 Like

See the "MultiMap" library.

It does piecewise linear interpolation, just like you want.


#include "MultiMap.h"

// Times[] holds minute mark for each target temperature
float Times[6] = {0, 30, 120, 150, 230, 260};

// Targets[] holds the target temperature at each time
float Targets[6]  = {20, 70, 70, 120, 120, 20};

unsigned long StartTime = 0;

void setup()
{
  StartTime = millis();  // Start a cycle
}


void loop()
{
  unsigned long elapsedMilliseconds = millis() - StartTime;
  float target;

  // If we reach the end of the cycle, use the last temperature
  if (elapsedMilliseconds / 60000.0 > (Times[5] * 60000ul))
    target = Targets[5];
  else
    target = multiMap<float>(elapsedMilliseconds / 60000.0, Times, Targets, 6);

  // Use PID to head to target temperature
  
}
1 Like

Thanks! This will lead to a jump in temperature instead of a linear change but I really want to understand structs. Could you explain what this for statement does?

for (Stage const& stage: stages)

This is how I did it to achieve linear change.

float getTargetTemp() {
  if (elapsedMillis / 60000 < 30) {
    return elapsedMillis / 60000.0 * (5.0 / 3) + 20;
  }
  else if (elapsedMillis / 60000 < 120) {
    return 70;
  }
else if ...
}

Thank you, never thought of using a library. Feels a little bit like cheating as I'm doing this mainly for the fun of learning something new but this looks like it does exactly what i want.

It loops over all objects in the stages array. The following,

for (Stage const& stage: stages) {
  // Do something with `stage`.
}

does more or less the same as this:

for (size_t i = 0; i < sizeof(stages) / sizeof(stages[0]); i++) {
  // Do something with `stages[i]`.
}

I assumed that physics would take care of the interpolation. If this is not the case (when you have a very low thermal mass for example), you could use map to interpolate between two consecutive data points or you could use the library @johnwasser suggested (I would go for the latter).

1 Like

You can look at the library sources and learn how the library does the interpolation. That would be learning something new. :slight_smile:

2 Likes

One more question: I ran your code and learned about ranged based loops. Only thing I don't understand is the ampersand. I guess it is a "reference parameter" but I don't understand what it does. What is it referring to?

It makes the loop variable ('stage' in this case) a reference to each element in the array (one at a time) rather than a copy of it. This doesn't make a difference for arrays of simple things like ints and floats. But, it could make a huge difference for complex classes.

1 Like

To add to what @gfvalvo said, you also want to use a reference when you want to change the iterable structure.

int arr[] = {1, 2, 3};

for (int a: arr) {
  a++;
}
for (int a: arr) {
  Serial.print(a);
}
// Prints "123".

for (int& a: arr) {
  a++;
}
for (int a: arr) {
  Serial.print(a);
}
// Prints "234".
1 Like

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