Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /********* Pleasedontcode.com **********
- Pleasedontcode thanks you for automatic code generation! Enjoy your code!
- - Terms and Conditions:
- You have a non-exclusive, revocable, worldwide, royalty-free license
- for personal and commercial use. Attribution is optional; modifications
- are allowed, but you're responsible for code maintenance. We're not
- liable for any loss or damage. For full terms,
- please visit pleasedontcode.com/termsandconditions.
- - Project: # Thermal Desoldering
- - Source Code NOT compiled for: Arduino Pro Mini 5V
- - Source Code created on: 2026-02-27 13:08:59
- ********* Pleasedontcode.com **********/
- /****** SYSTEM REQUIREMENTS *****/
- /****** SYSTEM REQUIREMENT 1 *****/
- /* Remova o debug serial a fim de reduzir memoria. */
- /****** END SYSTEM REQUIREMENTS *****/
- /* START CODE */
- /*
- ============================================
- SOPRADOR TÉRMICO PARA DESSOLDAGEM v4.1 AC
- ============================================
- ✅ REFATORADO - SEM CONFLITOS DE PINOS
- ✅ PIN D2 DETECÇÃO ZERO-CROSSING
- ✅ PIN D3 PWM HEATER_GATE - MOC3023 LED (Resistência AC)
- ✅ PIN D12 PWM PUMP_GATE - MOC3023 LED (Bomba AC)
- ✅ PROTEÇÃO: BOMBA SOLENOIDE COM LIMITE DE 40% MÍNIMO
- ✅ SERIAL DEBUG REMOVIDO - OTIMIZADO PARA MEMORIA
- ⚡ SISTEMA AC COMPLETO:
- - MOC3023 + BT136 (Triacs)
- - Detecção zero-crossing
- - Controle de resistência AC via PWM HEATER_GATE (D3)
- - Controle de bomba solenoide AC via PWM PUMP_GATE (D12)
- - Termopar MAX6675 Tipo K
- - Display ST7735S 1.8" (D8=DC, D9=RST, D10=CS)
- - EEPROM para salvar setpoint
- ============================================
- */
- /****** DEFINITION OF LIBRARIES *****/
- #include <Adafruit_GFX.h> //https://github.com/adafruit/Adafruit-GFX-Library
- #include <Adafruit_ST7735.h>
- #include <SPI.h>
- #include <max6675.h>
- #include <EEPROM.h>
- // ======================================
- // CONFIGURAÇÃO DE PINOS (LIMPO)
- // ======================================
- // Display ST7735S (SPI)
- #define TFT_CS 10
- #define TFT_RST 9
- #define TFT_DC 8
- // Termopar MAX6675 (SPI)
- #define THERMO_SCK 11
- #define THERMO_CS 5
- #define THERMO_SO 4
- // Potenciômetros analógicos
- #define POT_TEMP A0
- #define POT_FAN A1
- // Botão
- #define POWER_BTN 7
- // ⚡ PINOS AC - MOC3023 + BT136
- #define ZERO_CROSS_PIN 2 // INT0 - Detecção zero-crossing
- #define HEATER_GATE 3 // PWM - LED do MOC3023 (Resistência AC)
- #define PUMP_GATE 12 // PWM - LED do MOC3023 (Bomba AC)
- // ======================================
- // CONSTANTES DO SISTEMA
- // ======================================
- #define SAFE_TEMP 50
- #define AMBIENT_TEMP 25
- #define TEMP_MIN 100
- #define TEMP_MAX 500
- #define MAX_TEMP_ABSOLUTE 450
- #define MAX_HEATING_TIME 600000 // 10 minutos
- // ⚠️ PROTEÇÃO BOMBA SOLENOIDE
- #define PUMP_MIN_POWER 40 // Mínimo 40% para não travar
- #define PUMP_MAX_POWER 100 // Máximo 100%
- // PID Constants
- #define KP 6.0
- #define KI 0.15
- #define KD 1.5
- // ✨ CORES DO DISPLAY (RGB565)
- #define COLOR_BG 0x0000 // Preto
- #define COLOR_TEXT 0xFFFF // Branco
- #define COLOR_WHITE 0xFFFF // Branco
- #define COLOR_CYAN 0x07FF // Ciano
- #define COLOR_ORANGE 0xFE60 // Laranja
- #define COLOR_GREEN 0x0FE0 // Verde
- #define COLOR_RED 0xF800 // Vermelho
- #define COLOR_YELLOW 0xFFE0 // Amarelo
- #define EEPROM_SETPOINT 0
- // ======================================
- // INSTÂNCIAS
- // ======================================
- Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
- MAX6675 thermocouple(THERMO_SCK, THERMO_CS, THERMO_SO);
- // ======================================
- // VARIÁVEIS AC
- // ======================================
- volatile unsigned long lastZeroCrossing = 0;
- volatile bool zeroCrossingDetected = false;
- int heaterPowerLevel = 0; // 0-255 (nível de potência da resistência via HEATER_GATE PWM)
- int pumpPowerLevel = 0; // 0-255 (nível de potência da bomba via PUMP_GATE PWM)
- bool systemOn = false;
- bool heatingMode = false;
- bool coolingDown = false;
- bool tempReached = false;
- // ======================================
- // VARIÁVEIS DE TEMPERATURA E SETPOINT
- // ======================================
- float currentTemp = AMBIENT_TEMP;
- float lastValidTemp = AMBIENT_TEMP;
- int setpointTemp = 350;
- int fanSpeed = 50;
- // ======================================
- // CONTROLE PID
- // ======================================
- float pidIntegral = 0;
- float pidLastError = 0;
- // ======================================
- // TIMING
- // ======================================
- unsigned long lastUpdate = 0;
- unsigned long lastPidTime = 0;
- unsigned long lastButtonCheck = 0;
- unsigned long lastThermoRead = 0;
- unsigned long heatingStartTime = 0;
- // ======================================
- // CACHE DO DISPLAY (anti-flickering)
- // ======================================
- struct DisplayCache {
- int lastTemp;
- int lastSetpoint;
- int lastFanSpeed;
- int lastHeaterPower;
- int lastPumpPower;
- bool lastCoolingDown;
- uint16_t lastTempColor;
- int lastStatus;
- int lastTime;
- } cache;
- /****** FUNCTION PROTOTYPES *****/
- void setup(void);
- void loop(void);
- void zeroCrossingInterrupt(void);
- void checkPowerButton(unsigned long currentMillis);
- void readPotentiometers(void);
- void controlSystemAC(unsigned long currentMillis);
- void checkSafetyLimits(unsigned long currentMillis);
- int calculatePID(float setpoint, float current);
- void togglePower(void);
- void loadSettingsFromEEPROM(void);
- void saveSettingsToEEPROM(void);
- void drawBootScreen(void);
- void drawInterface(void);
- void updateDisplay(unsigned long currentMillis);
- void drawStatus(void);
- uint16_t getTempColor(float temp);
- void drawProgressBar(uint16_t color);
- void drawReachedIndicator(void);
- /****** DEFINITION OF LIBRARIES CLASS INSTANCES*****/
- // ======================================
- // SETUP - INICIALIZAÇÃO
- // ======================================
- void setup() {
- // Configurar pinos DIGITAIS
- pinMode(ZERO_CROSS_PIN, INPUT);
- pinMode(HEATER_GATE, OUTPUT);
- pinMode(PUMP_GATE, OUTPUT);
- pinMode(POWER_BTN, INPUT_PULLUP);
- pinMode(POT_TEMP, INPUT);
- pinMode(POT_FAN, INPUT);
- // Inicializar saídas em LOW (TRIACs desligados)
- digitalWrite(HEATER_GATE, LOW);
- digitalWrite(PUMP_GATE, LOW);
- analogWrite(HEATER_GATE, 0);
- analogWrite(PUMP_GATE, 0);
- // Inicializar display
- tft.initR(INITR_BLACKTAB);
- tft.setRotation(1);
- tft.fillScreen(COLOR_BG);
- drawBootScreen();
- delay(2000);
- // Carregar setpoint da EEPROM
- loadSettingsFromEEPROM();
- // Ler temperatura inicial
- currentTemp = thermocouple.readCelsius();
- if (!isnan(currentTemp)) {
- lastValidTemp = currentTemp;
- } else {
- currentTemp = AMBIENT_TEMP;
- }
- // ⚡ CONFIGURAR INTERRUPÇÃO ZERO-CROSSING
- attachInterrupt(digitalPinToInterrupt(ZERO_CROSS_PIN), zeroCrossingInterrupt, RISING);
- drawInterface();
- lastThermoRead = millis();
- }
- // ======================================
- // LOOP PRINCIPAL
- // ======================================
- void loop() {
- unsigned long currentMillis = millis();
- checkPowerButton(currentMillis);
- readPotentiometers();
- // Ler termopar
- if (currentMillis - lastThermoRead >= 500) {
- float temp = thermocouple.readCelsius();
- if (!isnan(temp) && temp > -10 && temp < 600) {
- currentTemp = temp;
- lastValidTemp = temp;
- }
- lastThermoRead = currentMillis;
- }
- controlSystemAC(currentMillis);
- checkSafetyLimits(currentMillis);
- // Atualizar display com controle de flickering
- if (currentMillis - lastUpdate >= 200) {
- updateDisplay(currentMillis);
- lastUpdate = currentMillis;
- }
- }
- // ======================================
- // ⚡ INTERRUPÇÃO ZERO-CROSSING
- // ======================================
- void zeroCrossingInterrupt() {
- lastZeroCrossing = micros();
- zeroCrossingDetected = true;
- // ⚡ DISPARO DOS TRIACs COM DELAY PARA CONTROLE DE POTÊNCIA
- // Resistência (PIN 3 - HEATER_GATE PWM)
- if (heaterPowerLevel > 0) {
- delayMicroseconds(map(255 - heaterPowerLevel, 0, 255, 0, 8000));
- digitalWrite(HEATER_GATE, HIGH);
- delayMicroseconds(10);
- digitalWrite(HEATER_GATE, LOW);
- }
- // Bomba (PIN 12 - PUMP_GATE PWM)
- if (pumpPowerLevel > 0) {
- delayMicroseconds(map(255 - pumpPowerLevel, 0, 255, 0, 8000));
- digitalWrite(PUMP_GATE, HIGH);
- delayMicroseconds(10);
- digitalWrite(PUMP_GATE, LOW);
- }
- }
- // ======================================
- // VERIFICAR BOTÃO DE POTÊNCIA
- // ======================================
- void checkPowerButton(unsigned long currentMillis) {
- static bool buttonPressed = false;
- if (currentMillis - lastButtonCheck < 200) {
- return;
- }
- bool buttonState = digitalRead(POWER_BTN);
- if (buttonState == LOW && !buttonPressed) {
- buttonPressed = true;
- togglePower();
- }
- else if (buttonState == HIGH && buttonPressed) {
- buttonPressed = false;
- }
- lastButtonCheck = currentMillis;
- }
- // ======================================
- // ⚠️ LEITURA DE POTENCIÔMETROS
- // ======================================
- void readPotentiometers() {
- // Temperatura
- int tempRaw = analogRead(POT_TEMP);
- int newSetpoint = map(tempRaw, 0, 1023, TEMP_MIN, TEMP_MAX);
- if (abs(newSetpoint - setpointTemp) > 5) {
- setpointTemp = newSetpoint;
- saveSettingsToEEPROM();
- tempReached = false;
- }
- // Ventoinha (Bomba)
- int fanRaw = analogRead(POT_FAN);
- int rawFanSpeed = map(fanRaw, 0, 1023, 0, 100);
- // ⚠️ PROTEÇÃO: Bomba solenoide não pode oscilar abaixo de 40%
- // Se ficar abaixo, desliga completamente (0%) para não travar
- if (rawFanSpeed > 0 && rawFanSpeed < PUMP_MIN_POWER) {
- fanSpeed = 0; // Desliga bomba
- } else {
- fanSpeed = rawFanSpeed;
- }
- }
- // ======================================
- // ⚡ CONTROLE DO SISTEMA AC
- // ======================================
- void controlSystemAC(unsigned long currentMillis) {
- if (systemOn && heatingMode) {
- // Verificar tempo máximo de aquecimento
- if (currentMillis - heatingStartTime > MAX_HEATING_TIME) {
- togglePower();
- return;
- }
- // ⚡ PID para resistência AC (usando HEATER_GATE PWM = D3)
- if (currentMillis - lastPidTime >= 100) {
- heaterPowerLevel = calculatePID(setpointTemp, currentTemp);
- analogWrite(HEATER_GATE, map(heaterPowerLevel, 0, 255, 0, 200));
- lastPidTime = currentMillis;
- }
- // Verificar se chegou no setpoint
- if (abs(currentTemp - setpointTemp) < 3) {
- if (!tempReached) {
- tempReached = true;
- }
- }
- // ⚡ Controlar bomba AC (velocidade via potência) com proteção
- if (fanSpeed == 0) {
- pumpPowerLevel = 0;
- } else if (fanSpeed < PUMP_MIN_POWER) {
- pumpPowerLevel = 0; // Proteção: desliga se < 40%
- } else {
- pumpPowerLevel = map(fanSpeed, 0, 100, 0, 255);
- }
- } else if (coolingDown) {
- heaterPowerLevel = 0;
- analogWrite(HEATER_GATE, 0);
- if (currentTemp > SAFE_TEMP) {
- // ⚡ Bomba em máxima potência para resfriar
- pumpPowerLevel = 255;
- } else {
- pumpPowerLevel = 0;
- coolingDown = false;
- tempReached = false;
- }
- } else {
- heaterPowerLevel = 0;
- pumpPowerLevel = 0;
- analogWrite(HEATER_GATE, 0);
- }
- }
- // ======================================
- // ⚠️ VERIFICAR LIMITES DE SEGURANÇA
- // ======================================
- void checkSafetyLimits(unsigned long currentMillis) {
- if (currentTemp > MAX_TEMP_ABSOLUTE) {
- systemOn = false;
- heatingMode = false;
- coolingDown = true;
- heaterPowerLevel = 0;
- analogWrite(HEATER_GATE, 0);
- pumpPowerLevel = 255; // Máximo resfriamento
- pidIntegral = 0;
- pidLastError = 0;
- }
- }
- // ======================================
- // ⚡ CALCULA PID (RESISTÊNCIA AC)
- // ======================================
- int calculatePID(float setpoint, float current) {
- float error = setpoint - current;
- float deltaTime = 0.1;
- // Proporcional
- float P = KP * error;
- // Integral
- pidIntegral += error * deltaTime;
- pidIntegral = constrain(pidIntegral, 0, 255);
- float I = KI * pidIntegral;
- // Derivativo
- float derivative = (error - pidLastError) / deltaTime;
- float D = KD * derivative;
- pidLastError = error;
- // Output
- float output = P + I + D;
- output = constrain(output, 0, 255);
- // Anti-overshoot quando perto do setpoint
- if (abs(error) < 10 && current > setpoint) {
- output *= 0.5;
- }
- return (int)output;
- }
- // ======================================
- // LIGAR/DESLIGAR SISTEMA
- // ======================================
- void togglePower() {
- systemOn = !systemOn;
- pidIntegral = 0;
- pidLastError = 0;
- tempReached = false;
- if (systemOn) {
- heatingMode = true;
- coolingDown = false;
- heatingStartTime = millis();
- } else {
- heatingMode = false;
- coolingDown = true;
- }
- drawInterface();
- }
- // ======================================
- // EEPROM - SALVAR/CARREGAR SETPOINT
- // ======================================
- void loadSettingsFromEEPROM() {
- int savedSetpoint = EEPROM.read(EEPROM_SETPOINT) * 256 + EEPROM.read(EEPROM_SETPOINT + 1);
- if (savedSetpoint >= TEMP_MIN && savedSetpoint <= TEMP_MAX) {
- setpointTemp = savedSetpoint;
- }
- }
- void saveSettingsToEEPROM() {
- EEPROM.write(EEPROM_SETPOINT, setpointTemp / 256);
- EEPROM.write(EEPROM_SETPOINT + 1, setpointTemp % 256);
- }
- // ======================================
- // DESENHAR TELA DE BOOT
- // ======================================
- void drawBootScreen() {
- tft.fillScreen(COLOR_BG);
- tft.setTextSize(2);
- tft.setTextColor(COLOR_CYAN);
- tft.setCursor(10, 30);
- tft.println("SOPRADOR");
- tft.setCursor(20, 50);
- tft.println("TERMICO");
- tft.setTextSize(1);
- tft.setTextColor(COLOR_WHITE);
- tft.setCursor(15, 80);
- tft.println("v4.1 AC MOC3023");
- tft.setCursor(10, 95);
- tft.println("D3: HEATER, D12: PUMP");
- tft.drawRect(20, 110, 120, 8, COLOR_CYAN);
- for (int i = 0; i < 120; i += 3) {
- tft.fillRect(20, 110, i, 8, COLOR_CYAN);
- delay(10);
- }
- }
- // ======================================
- // DESENHAR INTERFACE PRINCIPAL
- // ======================================
- void drawInterface() {
- tft.fillScreen(COLOR_BG);
- // Cabeçalho
- tft.fillRect(0, 0, 160, 15, COLOR_CYAN);
- tft.setTextSize(1);
- tft.setTextColor(COLOR_BG);
- tft.setCursor(10, 4);
- tft.println("TERMOPAR K v4.1 AC");
- // Labels
- tft.setTextColor(COLOR_WHITE);
- tft.setTextSize(1);
- tft.setCursor(5, 25);
- tft.println("TEMPERATURA:");
- tft.setCursor(5, 65);
- tft.println("SETPOINT:");
- tft.setCursor(5, 85);
- tft.println("BOMBA AC (40% min):");
- tft.setCursor(5, 105);
- tft.print("Time: --:--");
- // Inicializar cache
- cache.lastTemp = -999;
- cache.lastSetpoint = -999;
- cache.lastFanSpeed = -999;
- cache.lastHeaterPower = -999;
- cache.lastPumpPower = -999;
- cache.lastCoolingDown = false;
- cache.lastTempColor = COLOR_CYAN;
- cache.lastStatus = -1;
- cache.lastTime = -1;
- }
- // ======================================
- // ATUALIZAR DISPLAY (COM ANTI-FLICKERING)
- // ======================================
- void updateDisplay(unsigned long currentMillis) {
- drawStatus();
- // Temperatura
- int displayTemp = (int)currentTemp;
- uint16_t tempColor = getTempColor(currentTemp);
- if (displayTemp != cache.lastTemp || tempColor != cache.lastTempColor) {
- tft.fillRect(5, 38, 150, 20, COLOR_BG);
- tft.setTextSize(3);
- tft.setTextColor(tempColor);
- tft.setCursor(10, 38);
- tft.print(displayTemp);
- tft.print("C");
- cache.lastTemp = displayTemp;
- cache.lastTempColor = tempColor;
- }
- // Setpoint
- if (setpointTemp != cache.lastSetpoint) {
- tft.fillRect(75, 65, 80, 15, COLOR_BG);
- tft.setTextSize(2);
- tft.setTextColor(COLOR_ORANGE);
- tft.setCursor(80, 65);
- tft.print(setpointTemp);
- tft.print("C");
- cache.lastSetpoint = setpointTemp;
- }
- // ⚡ BOMBA AC com proteção 40%
- if (fanSpeed != cache.lastFanSpeed || coolingDown != cache.lastCoolingDown) {
- tft.fillRect(5, 97, 150, 15, COLOR_BG);
- tft.setTextSize(2);
- if (coolingDown && currentTemp > SAFE_TEMP) {
- tft.setTextColor(COLOR_RED);
- tft.setCursor(10, 97);
- tft.print("MAX COOLING");
- } else if (fanSpeed == 0) {
- // ⚠️ Mostrar quando bomba está desligada por proteção
- tft.setTextColor(COLOR_RED);
- tft.setCursor(10, 97);
- tft.print("OFF (< 40%)");
- } else if (fanSpeed < PUMP_MIN_POWER) {
- tft.setTextColor(COLOR_RED);
- tft.setCursor(10, 97);
- tft.print("PROT!");
- } else {
- tft.setTextColor(COLOR_GREEN);
- tft.setCursor(10, 97);
- tft.print(fanSpeed);
- tft.print("% OK");
- }
- cache.lastFanSpeed = fanSpeed;
- cache.lastCoolingDown = coolingDown;
- }
- // Timer
- if (systemOn && heatingMode) {
- int elapsedSeconds = (currentMillis - heatingStartTime) / 1000;
- int minutes = elapsedSeconds / 60;
- int seconds = elapsedSeconds % 60;
- if (elapsedSeconds != cache.lastTime) {
- tft.fillRect(50, 105, 50, 10, COLOR_BG);
- tft.setTextSize(1);
- tft.setTextColor(COLOR_YELLOW);
- tft.setCursor(55, 105);
- if (minutes < 10) tft.print("0");
- tft.print(minutes);
- tft.print(":");
- if (seconds < 10) tft.print("0");
- tft.print(seconds);
- cache.lastTime = elapsedSeconds;
- }
- }
- drawProgressBar(tempColor);
- // Indicador "TEMPERATURA ATINGIDA!"
- if (tempReached && abs(currentTemp - setpointTemp) < 5) {
- drawReachedIndicator();
- }
- }
- // ======================================
- // DESENHAR STATUS
- // ======================================
- void drawStatus() {
- tft.fillRect(115, 2, 42, 11, COLOR_CYAN);
- tft.setTextSize(1);
- tft.setTextColor(COLOR_BG);
- tft.setCursor(117, 4);
- if (coolingDown) {
- tft.println("RESFR");
- } else if (systemOn && heatingMode) {
- if (tempReached) {
- tft.println("OK!");
- } else if (currentTemp < setpointTemp) {
- tft.println("AQUEC");
- } else {
- tft.println("AJUST");
- }
- } else {
- tft.println("DESLIG");
- }
- }
- // ======================================
- // OBTER COR BASEADA NA TEMPERATURA
- // ======================================
- uint16_t getTempColor(float temp) {
- if (temp > 400) {
- return COLOR_RED;
- } else if (temp > 200) {
- return COLOR_ORANGE;
- } else if (temp > 100) {
- return COLOR_YELLOW;
- } else {
- return COLOR_CYAN;
- }
- }
- // ======================================
- // DESENHAR BARRA DE PROGRESSO TÉRMICA
- // ======================================
- void drawProgressBar(uint16_t color) {
- tft.drawRect(5, 120, 150, 8, COLOR_WHITE);
- int progress = map(constrain((int)currentTemp, AMBIENT_TEMP, setpointTemp), AMBIENT_TEMP, setpointTemp, 0, 148);
- if (progress > 0) {
- tft.fillRect(6, 121, progress, 6, color);
- }
- if (progress < 148) {
- tft.fillRect(6 + progress, 121, 148 - progress, 6, COLOR_BG);
- }
- }
- // ======================================
- // INDICADOR "TEMPERATURA ATINGIDA!"
- // ======================================
- void drawReachedIndicator() {
- static unsigned long lastBlink = 0;
- static bool blinkState = false;
- if (millis() - lastBlink > 500) {
- blinkState = !blinkState;
- lastBlink = millis();
- }
- if (blinkState) {
- tft.fillRect(120, 100, 35, 20, COLOR_GREEN);
- tft.setTextSize(2);
- tft.setTextColor(COLOR_BG);
- tft.setCursor(122, 104);
- tft.println("OK!");
- }
- }
- /* END CODE */
Advertisement
Add Comment
Please, Sign In to add comment