From 5591b3d85085bae03ec01b9ad57577359877e7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Lecomte?= Date: Mon, 14 Oct 2019 14:38:53 +0200 Subject: [PATCH 1/2] Faster temperature sampling and moving average There is a large noise in the temperature readings. That makes the projected temperatures unreliable. Here we add a moving average of 100 samples on top of the temperature readings. The value of 100 was found by looking at the data from previous runs. The temperature readings are done every 10 ms. Assuming the noise is high-frequency, this should produce smooth temperature readings. The added latency is no more than 1 second, which is negligible. The heatings controls are updated every 2 seconds. The logic is not changed. We add an element to the serial output to log which heater has been chosen. --- crepemaker.ino | 128 ++++++++++++++++++++++++++++++++++++------------- user.h | 13 +++-- 2 files changed, 104 insertions(+), 37 deletions(-) diff --git a/crepemaker.ino b/crepemaker.ino index 79ba9a5..ea10fd9 100644 --- a/crepemaker.ino +++ b/crepemaker.ino @@ -6,19 +6,26 @@ // libraries to be installed from the library manager #include "max6675.h" -// other compile directives -#define LOOPTIME 500 // cycle time, in ms - // global variables and objects MAX6675 thermocouple1(CLKPIN, CS1PIN, DOPIN); MAX6675 thermocouple2(CLKPIN, CS2PIN, DOPIN); +// variables for the heating control float previoustemp1 = 0; float previoustemp2 = 0; -unsigned long timestamp = 0; -int counter = 0; - -void seriallogger(float temp1, float temp2, float ror1, float ror2, float projectedtemp1, float projectedtemp2) { +unsigned long lastControlUpdate = 0; // last time controls were updated + +// variables for the moving averages on temperature readings +float tempReadings1[NUMREADINGS]; // the temperature readings from the analog input +float tempReadings2[NUMREADINGS]; +int readIndex = 0; // the index of the current reading +unsigned long lastRead = 0; // last time readings were updated +float tempReadingsTotal1 = 0; // the running total +float tempReadingsTotal2 = 0; +float temp1; // the average +float temp2; + +void seriallogger(float temp1, float temp2, float ror1, float ror2, float projectedtemp1, float projectedtemp2, byte control) { Serial.print(temp1); Serial.print(","); Serial.print(temp2); @@ -29,7 +36,9 @@ void seriallogger(float temp1, float temp2, float ror1, float ror2, float projec Serial.print(","); Serial.print(projectedtemp1); Serial.print(","); - Serial.println(projectedtemp2); + Serial.print(projectedtemp2); + Serial.print(","); + Serial.println(control); } void setup() { @@ -37,34 +46,82 @@ void setup() { pinMode(SSR2PIN, OUTPUT); Serial.begin(BAUD); - + // wait for MAX chips to stabilize delay(500); + + // initialize the readings array to zero + for (int i = 0; i < NUMREADINGS; i++) { + tempReadings1[i] = 0; + tempReadings2[i] = 0; + } + + // initialize the temperature readings and averages with actual values + while (readIndex < NUMREADINGS - 1) { + updateTemperatureReadings(); + delay(LOOP_PERIOD_MS); + } +} + +void loop() { + updateTemperatureReadings(); + updateControls(); + delay(LOOP_PERIOD_MS); +} + +void updateTemperatureReadings() { + if (millis() - lastRead < READING_PERIOD_MS) { + return; + } + + lastRead += READING_PERIOD_MS; + + // subtract the last reading + tempReadingsTotal1 = tempReadingsTotal1 - tempReadings1[readIndex]; + tempReadingsTotal2 = tempReadingsTotal2 - tempReadings2[readIndex]; + + // read from the sensor + tempReadings1[readIndex] = thermocouple1.readCelsius(); + tempReadings2[readIndex] = thermocouple2.readCelsius(); + + // add the reading to the total + tempReadingsTotal1 = tempReadingsTotal1 + tempReadings1[readIndex]; + tempReadingsTotal2 = tempReadingsTotal2 + tempReadings2[readIndex]; + + // advance to the next position in the array + readIndex = readIndex + 1; + + // if we are at the end of the array... + if (readIndex >= NUMREADINGS) { + // ...wrap around to the beginning + readIndex = 0; + } + + // calculate the average + temp1 = tempReadingsTotal1 / NUMREADINGS; + temp2 = tempReadingsTotal2 / NUMREADINGS; } -void loop() { +void updateControls() +{ unsigned long timeelapsed = 0; - float temp1; - float temp2; float ror1; float ror2; float projectedtemp1; float projectedtemp2; - temp1 = thermocouple1.readCelsius(); - temp2 = thermocouple2.readCelsius(); - - if (counter >= SAMPLING) { - timeelapsed = (millis() - timestamp); - ror1 = (((temp1 - previoustemp1) / timeelapsed) * 1000 * 60); //Raise of rise in C/min - ror2 = (((temp2 - previoustemp2) / timeelapsed) * 1000 * 60); //Raise of rise in C/min - timestamp = millis(); - counter = 0; - previoustemp1 = temp1; // We should really calculate an average rather... - previoustemp2 = temp2; + timeelapsed = (millis() - lastControlUpdate); + + if (timeelapsed < CONTROL_UPDATE_PERIOD_MS) { + return; } - - counter++; + + lastControlUpdate += CONTROL_UPDATE_PERIOD_MS; // do not use timeelapsed here to avoid drift + + ror1 = (((temp1 - previoustemp1) / timeelapsed) * 1000 * 60); //Raise of rise in C/min + ror2 = (((temp2 - previoustemp2) / timeelapsed) * 1000 * 60); //Raise of rise in C/min + previoustemp1 = temp1; + previoustemp2 = temp2; if (ror1 >= 0) { projectedtemp1 = (temp1 + (ror1 * RISEINERTIA)); @@ -80,21 +137,24 @@ void loop() { projectedtemp2 = (temp2 + (ror2 * FALLINERTIA)); } - seriallogger(temp1,temp2,ror1,ror2,projectedtemp1,projectedtemp2); + byte control = 0; - if (projectedtemp1 <= SV1) { - digitalWrite(SSR2PIN, LOW); - digitalWrite(SSR1PIN, HIGH); - } + if (projectedtemp1 <= SV1) { + digitalWrite(SSR2PIN, LOW); + digitalWrite(SSR1PIN, HIGH); + control = 1; + } else { - digitalWrite(SSR1PIN, LOW); + digitalWrite(SSR1PIN, LOW); if (projectedtemp2 <= SV2) { - digitalWrite(SSR2PIN, HIGH); + digitalWrite(SSR2PIN, HIGH); + control = 2; } else { - digitalWrite(SSR2PIN, LOW); + digitalWrite(SSR2PIN, LOW); } } - delay(LOOPTIME); + + seriallogger(temp1, temp2, ror1, ror2, projectedtemp1, projectedtemp2, control); } diff --git a/user.h b/user.h index d43d8bb..f47666b 100644 --- a/user.h +++ b/user.h @@ -14,14 +14,21 @@ #define RISEINERTIA 3 //how long it takes the system to stop rising (in min) #define FALLINERTIA 1 //how long it takes the system to stop falling (in min) -#define SAMPLING 5 //loops to calculate ror +// cycle time, in ms +#define LOOP_PERIOD_MS 1 // Delay time between temperature readings // from the temperature sensor (ms). -#define DELAY_TIME 20 +// (must be larger than LOOP_PERIOD_MS) +#define READING_PERIOD_MS 10 // How many readings are taken to determine a mean temperature. -#define READINGS 10 +// accounts for the thermocouple noise +#define NUMREADINGS 100 + +// delay between 2 control updates +// (must be larger than LOOP_PERIOD_MS) +#define CONTROL_UPDATE_PERIOD_MS 2000 // Pin mapping // Common SPI pins From 8e8428257e25bfc28406c65d63a7e95ca66a1ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Lecomte?= Date: Mon, 14 Oct 2019 19:16:26 +0200 Subject: [PATCH 2/2] Adjust the reading settings MAX6675 needs 200 ms to convert the analog value to digital. Take that into account, increase the reading period, decrease the number of readings to be averaged. --- user.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/user.h b/user.h index f47666b..87ebd0d 100644 --- a/user.h +++ b/user.h @@ -20,11 +20,12 @@ // Delay time between temperature readings // from the temperature sensor (ms). // (must be larger than LOOP_PERIOD_MS) -#define READING_PERIOD_MS 10 +// MAX6675 takes about 200 ms to convert +#define READING_PERIOD_MS 200 // How many readings are taken to determine a mean temperature. // accounts for the thermocouple noise -#define NUMREADINGS 100 +#define NUMREADINGS 10 // delay between 2 control updates // (must be larger than LOOP_PERIOD_MS)