machines/extrusion/lydia-print-head-v3/firmware/firmware_last/TemperatureController.cpp

332 lines
7.6 KiB
C++

#include "TemperatureController.h"
#ifdef HAS_TC
short TemperatureController::setup()
{
return E_OK;
}
short TemperatureController::debug(Stream *stream)
{
*stream << this->name << "\n\t : ";
return E_OK;
}
short TemperatureController::info(Stream *stream)
{
*stream << this->name << "\n\t : ";
return E_OK;
}
short TemperatureController::loop()
{
updateTCP();
return E_OK;
}
void TemperatureController::updateTCP()
{
modbus->mb->R[MB_W_TC_STATE] = _state;
}
double PIDInput[2] = {0, 0};
double PIDOutput[2] = {0, 0};
double PIDSetpoint[2] = {0, 0};
double PIDKp[2]; // = DEFAULT_PID_KP;
double PIDKi[2]; // = DEFAULT_PID_KI;
double PIDKd[2]; // = DEFAULT_PID_KD;
double relayPin[2] = HEATER_PIN_RELAY;
double thermPin[2] = TEMP_PIN;
unsigned int targetTempHeater[PLASTIC_ID_TOTAL][2]; // = {DEFAULT_TEMP_PET, DEFAULT_TEMP_HDPE, DEFAULT_TEMP_V, DEFAULT_TEMP_LDPE, DEFAULT_TEMP_PP, DEFAULT_TEMP_PS, DEFAULT_TEMP_PLA};
unsigned long windowStartTime[2];
Oversample *Thermistor[2];
PID PIDHeater[2] = {{&PIDInput[0], &PIDOutput[0], &PIDSetpoint[0], PIDKp[0], PIDKi[0], PIDKd[0], DIRECT},
{&PIDInput[1], &PIDOutput[1], &PIDSetpoint[1], PIDKp[1], PIDKi[1], PIDKd[1], DIRECT}};
extern void initHeater()
{
for (int i = 0; i < 2; i++)
{
PIDHeater[i].SetMode(MANUAL);
PIDHeater[i].SetOutputLimits(0, SOFT_PWM_WINDOW_SIZE);
pinMode(relayPin[i], OUTPUT);
Thermistor[i] = new Oversample(thermPin[i], 14);
printPIDSetting(i);
}
pinMode(FAN_PIN, OUTPUT);
}
extern void updateTemp()
{
for (int i = 0; i < 2; i++)
{
PIDInput[i] = analog2temp(Thermistor[i]->readDecimated());
if ((int)PIDInput[i] < HEATER_MIN_TEMP || (int)PIDOutput[i] > HEATER_MAX_TEMP)
{
offHeater();
}
}
}
extern void updatePID()
{
for (int i = 0; i < 2; i++)
{
PIDHeater[i].Compute();
if ((int)PIDSetpoint[i] <= 0)
{
PIDOutput[i] = 0; // Override PID
PIDHeater[i].SetMode(MANUAL);
}
}
}
extern void manageHeaterSoftPWM()
{
unsigned long now = millis();
for (int i = 0; i < 2; i++)
{
if (now - windowStartTime[i] > SOFT_PWM_WINDOW_SIZE)
{
windowStartTime[i] = now;
}
if (PIDOutput[i] > now - windowStartTime[i])
{
digitalWrite(relayPin[i], HIGH);
}
else
{
digitalWrite(relayPin[i], LOW);
}
}
}
extern void AT(double temp, int hotend, int ncycles, bool set_result /*=false*/)
{
double input = 0.0;
int cycles = 0;
bool heating = true;
unsigned long temp_ms = millis(), t1 = temp_ms, t2 = temp_ms;
long t_high = 0, t_low = 0;
unsigned long ms = millis();
long bias, d;
double Ku, Tu;
double workKp = 0, workKi = 0, workKd = 0;
double max = 0, min = 10000;
#ifdef ENABLE_SERIAL
Serial.println("AT");
#endif
offHeater(); // switch off all heaters.
PIDOutput[hotend] = bias = d = SOFT_PWM_WINDOW_SIZE >> 1;
bool wait_for_heatup = true;
// PID Tuning loop
while (wait_for_heatup)
{
ms = millis();
updateTemp();
input = PIDInput[hotend];
if (max < input)
max = input;
if (min > input)
min = input;
if (heating && input > temp)
{
if (ms > t2 + 5000UL)
{
heating = false;
PIDOutput[hotend] = (bias - d) >> 1;
t1 = ms;
t_high = t1 - t2;
max = temp;
}
}
if (!heating && input < temp)
{
if (ms > t1 + 5000UL)
{
heating = true;
t2 = ms;
t_low = t2 - t1;
if (cycles > 0)
{
long max_pow = SOFT_PWM_WINDOW_SIZE;
bias += (d * (t_high - t_low)) / (t_low + t_high);
bias = constrain(bias, 20, max_pow - 20);
d = (bias > max_pow / 2) ? max_pow - 1 - bias : bias;
bias = SOFT_PWM_WINDOW_SIZE >> 1;
d = SOFT_PWM_WINDOW_SIZE >> 1; // Shek: hard-code fixed bias and d because the autocalculation resulted in thermal runaway (the power of "turning off" causes continuous heat up).
#ifdef ENABLE_SERIAL
Serial.print(F(SERIAL_AT_BIAS));
Serial.print(bias);
Serial.println();
Serial.print(F(SERIAL_AT_D));
Serial.print(d);
Serial.println();
Serial.print(F(SERIAL_AT_MIN));
Serial.print(min);
Serial.println();
Serial.print(F(SERIAL_AT_MAX));
Serial.print(max);
Serial.println();
#endif
if (cycles > 2)
{
Ku = (4.0 * d) / (M_PI * (max - min) * 0.5);
Tu = ((double)(t_low + t_high) * 0.001);
workKp = 0.6 * Ku;
workKi = 2 * workKp / Tu;
workKd = workKp * Tu * 0.125;
#ifdef ENABLE_SERIAL
Serial.print(F(SERIAL_AT_KU));
Serial.println(Ku);
Serial.print(F(SERIAL_AT_TU));
Serial.println(Tu);
Serial.println(F(SERIAL_AT_CLASSIC_PID));
Serial.print(F(SERIAL_AT_KP));
Serial.println(workKp);
Serial.print(F(SERIAL_AT_KI));
Serial.println(workKi);
Serial.print(F(SERIAL_AT_KD));
Serial.println(workKd);
#endif
}
}
PIDOutput[hotend] = (bias + d) >> 1;
cycles++;
min = temp;
}
}
#define MAX_OVERSHOOT_PID_AUTOTUNE 20
if (input > temp + MAX_OVERSHOOT_PID_AUTOTUNE)
{
#ifdef ENABLE_SERIAL
Serial.println(F(SERIAL_AT_OVERSHOOT));
#endif
return;
}
// Every 2 seconds...
if (ms > temp_ms + 2000UL)
{
#ifdef ENABLE_SERIAL
Serial.print("T");
Serial.print(hotend);
Serial.print(": ");
Serial.print(input);
Serial.print(" @: ");
Serial.println(PIDOutput[hotend]);
/*char tbuf[6], outbuf[19];
ftoa(tbuf,input,2);
sprintf_P(outbuf, PSTR(SERIAL_AT_TEMP_OUT), hotend, tbuf, (int) PIDOutput[hotend]);
Serial.println(outbuf);*/
#endif
temp_ms = ms;
}
// every 2 seconds
// Over 10 minutes?
if (((ms - t1) + (ms - t2)) > (10L * 60L * 1000L * 10L))
{
#ifdef ENABLE_SERIAL
Serial.println(F(SERIAL_AT_TIMEOUT));
#endif
return;
}
if (cycles > ncycles)
{
#ifdef ENABLE_SERIAL
Serial.println(F(SERIAL_AT_FINISH));
#endif
if (set_result)
{
PIDKp[hotend] = workKp;
PIDKi[hotend] = workKi;
PIDKd[hotend] = workKd;
PIDHeater[hotend].SetTunings(PIDKp[hotend], PIDKi[hotend], PIDKd[hotend]);
printPIDSetting(hotend);
}
// update PID etc.
return;
}
// lcd_update();
if (!wait_for_heatup)
{
offHeater();
}
else
{
manageHeaterSoftPWM();
}
}
}
extern void setFan(bool state)
{
digitalWrite(FAN_PIN, state);
}
extern void offHeater()
{
for (int i = 0; i < 2; i++)
{
PIDSetpoint[i] = 0;
PIDHeater[i].SetMode(MANUAL);
}
#ifdef ENABLE_LCD
statusLCD(F(STATUS_READY));
logoLCD(-1);
#endif
#ifdef ENABLE_SERIAL
Serial.println(SERIAL_OFF_ALL_HEATER);
#endif
}
extern void printPIDSetting(int idxHeater)
{
#ifdef ENABLE_SERIAL
Serial.print(F(SERIAL_PRINT_PID));
Serial.print(idxHeater);
Serial.print(F(":"));
Serial.print(PIDHeater[idxHeater].GetKp(), 2);
Serial.print(F(SERIAL_PRINT_KP));
Serial.print(PIDHeater[idxHeater].GetKi(), 2);
Serial.print(F(SERIAL_PRINT_KI));
Serial.print(PIDHeater[idxHeater].GetKd(), 2);
Serial.print(F(SERIAL_PRINT_KD));
Serial.println();
#endif
}
extern void setHeaterTemp(int idxHeater, int newSetpoint)
{
PIDSetpoint[idxHeater] = newSetpoint;
PIDHeater[idxHeater].SetMode((newSetpoint <= 0) ? MANUAL : AUTOMATIC);
// char serialbuffer[45];
// sprintf_P(serialbuffer, PSTR(SERIAL_SET_HEATER_TO_TEMP), idxHeater, newSetpoint);
//#ifdef ENABLE_SERIAL
// Serial.println(serialbuffer);
//#endif
}
#endif