PET filament with bottles, PID control failure, Arduino Nano

This text is being translated into English. My native language is Spanish.

A few days ago, I started a project to build a machine that converts PET bottles into PET filament for a 3D printer. Everything was going well until I had to write the PID control code myself, since all the examples online use MOSFETs. My problem is that I don’t have one, so I tried to make it work with a relay before buying one. The reality is that it works, but it works very poorly—the temperatures fluctuate WAY too much, making the filament quality terrible. Also, I had a lot of trouble with something as simple as measuring the temperature. I don’t know why, but I find it really difficult to get an accurate reading.

Data:

  • I am using a 100k thermistor (pin A0) (the same type used in 3D printers).
  • I am using a single-channel relay module, optocoupled with inverted logic, meaning that when it receives a HIGH (1), the relay turns off, and when it receives a LOW (0), the relay turns on.
  • I am using a 12V 40W heating cartridge.
  • 1.7mm nozzle.
  • The reference resistor I am using is 10k.
  • The target temperature set in the code is 220°C (428°F), but the correct operating range should be between 180°C and 220°C (356°F–428°F).

Problem:
I have to guess the temperature because the code returns an unstable reading, and on top of that, it fails to fully stabilize the temperature. This means I don’t really know how hot the hotend is, as the measurement could be off by several degrees.

Notes:
Besides being a beginner at this and having never done it before, several parts of the code were "fixed" with the help of ChatGPT, so I don’t fully understand what is failing.

#include <LiquidCrystal_I2C.h>
#include <PID_v1.h>
#include <Wire.h>

// Configuración del LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Pines y configuración
#define THERMISTOR_PIN A0
#define RELAY_PIN 5

// Variables PID
double Setpoint = 220.0; // Temperatura objetivo en grados Celsius
double Input;            // Temperatura medida
double Output;           // Salida PID

// Coeficientes del PID
double Kp = 2.0, Ki = 5.0, Kd = 1.0;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

// Configuración del divisor de tensión
#define SERIES_RESISTOR 10000     // Resistencia en ohmios (ajustada)
#define THERMISTOR_NOMINAL 100000 // Resistencia nominal del termistor
#define TEMPERATURE_NOMINAL 25    // Temperatura nominal del termistor
#define BCOEFFICIENT 3950         // Coeficiente beta del termistor
#define ADC_MAX 1023.0            // Resolución ADC
#define VCC 5.0                   // Voltaje de referencia

void setup() {
  // Configuración de pines
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW); // Apaga el relay inicialmente

  // Inicialización del LCD
  lcd.init();
  lcd.backlight();
  lcd.print("Iniciando...");

  // Configuración del PID
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(0, 255); // Límite de salida para PWM o activación

  delay(2000);
  lcd.clear();
}

void loop() {
  // Leer el termistor
  int adcValue = analogRead(THERMISTOR_PIN);

  // Corregir cálculo de resistencia
  double voltage = adcValue * (VCC / ADC_MAX);
  double resistance = (VCC * SERIES_RESISTOR / voltage) - SERIES_RESISTOR;

  // Calcular la temperatura en grados Celsius
  double steinhart;
  steinhart = resistance / THERMISTOR_NOMINAL;      // (R/Ro)
  steinhart = log(steinhart);                      // ln(R/Ro)
  steinhart /= BCOEFFICIENT;                       // 1/B * ln(R/Ro)
  steinhart += 1.0 / (TEMPERATURE_NOMINAL + 273.15); // + (1/To)
  steinhart = 1.0 / steinhart;                     // Invertir
  Input = steinhart - 273.15;                      // Convertir a Celsius

  // Actualizar el PID
  myPID.Compute();

  // Control del relay con histeresis para mayor precisión
  static bool relayState = false;
  if (!relayState && Input < (Setpoint - 2)) {
    relayState = true;
    digitalWrite(RELAY_PIN, HIGH);
  } else if (relayState && Input > (Setpoint + 2)) {
    relayState = false;
    digitalWrite(RELAY_PIN, LOW);
  }

  // Mostrar en el LCD
  lcd.setCursor(0, 0);
  lcd.print("Temp: ");
  lcd.print(Input, 1); // Una cifra decimal
  lcd.print(" C   ");

  lcd.setCursor(0, 1);
  lcd.print("Set:  ");
  lcd.print(Setpoint, 1);
  lcd.print(" C   ");

  delay(500); // Tiempo de actualización
}

Thank you for your help, I am at your disposal.

For accurate temperature control you would need a pwm driven heater (with a mosfet or an SSR (depending on the type of heater) not a relay).
Now you have a 4 degree hysteresis... that will easily lead to undershoot/overshoot.
You need to measure temperature close to where the heater is... otherwise your controller will always be late (causing temp swings).
You need a stable temp measurement. Get that done first. You might need a low pass filter. Maybe a 100 nF capacitor from analog input to gnd could already do the trick. Or you might need to shield cables. Can you share a picture of your setup?
How did you set your pid parameters?
It seems you do not use PID at all...
Can you print the value read from your sensor to Serial? Or to LCD? 10k seems too low for accurate reading. Or, 100k thermistor is too high for accurate readings.

1 Like

I don't know how much knowledge there is in the spanish sub-forum.
But there are multiple spanish subforums. So there seems a lot knowledge there too.
So may it will be easier if you post their in spanish.

How fast is the instability? Does it swing back and forth in seconds tens of seconds?

If you cannot measure the temperature reliably, controlling it will be impossible.

If the temperature changes too fast to measure, you may need to slow the system down with increased thermal mass or a less powerful heater.

Without data, it is difficult to give better advice.

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