duminică, 1 martie 2026

O simpla statie de lipit cu Arduino

 

Va salut !

Va prezint statia mea de lipit cu Arduino. Realizata cu AI Gemini ; eu doar am formulat cerintele si am testat codul, deci nu am prea multe merite. Inspiratia mea au fost nenumaratele exemple existente.

Pe scurt : ciocan Pensol SL-10 (cel pe care-l am de mai bine de 15 ani si nu renunt la el, e prea bun !), Arduino Nano, display LED 7 segmente 3 digiti, encoder rotativ, 2 butoane pentru memorare 2 temperaturi de lucru, stand-by dupa 30 minute.

Am testat-o cateva zile, pe parcursul carora am rezolvat diverse buguri. In prezent functioneza destul de bine, zic eu. Am vrut initial cu PID, dar inertia termica mare a ciocanului m-a facut sa renunt.

Orice comentariu sau sugestie sunt binevenite. Statia este prezentata si pe Elforum (https://www.elforum.info/topic/165506-statie-de-lipit-cu-arduino/), cine vrea o poate modifica dupa cum doreste (recomand Vercel / Perplexity / Qwen / Gemini, nu neaparat in aceasta ordine). Numai bine ! 

 

Dupa cateva zile de folosire mai intensa, am mai facut cateva modificari in soft. 



 

 

 


/*
//
// STATIE DE LIPIT CU ARDUINO
// DISPLAY LED CU 7 SEGMENTE
// ENCODER SI 2 MEMORII
// STAND-BY DUPA 30 MINUTE
// pt PENSOL SL-10
// Vers.finala-03.2026
//
//
// mai multe info aici :
//
// www.elforum.info/topic/165506-statie-de-lipit-cu-arduino
*/


/*
// STATIE DE LIPIT CU ARDUINO - VERSIUNEA OPTIMIZATĂ PENTRU PENSOL SL-10
// REVIZUITĂ PENTRU PRECIZIE +/- 5 GRADE ȘI ZERO FLICKER
*/
#include "max6675.h"
#include <Encoder.h>
#include <EEPROM.h>
#include <EasyButton.h>
#include <avr/wdt.h>

// --- CONFIGURARE HARDWARE (PĂSTRATĂ) ---
MAX6675 thermocouple(10, 9, 8);
Encoder myEnc(2, 3);
const int segPins[] = {4, 5, 6, 7, 12, 13, A0};
const int digitPins[] = {A3, A4, A5};
const int heaterPin = 11;

// --- VARIABILE CONTROL ---
double Setpoint = 350.0;
double Input = 0;
int pwm = 0;
long oldPosition = 0;
unsigned long lastUpdate = 0;
unsigned long lastEncoderTime = 0;
unsigned long lastAdjustTime = 0;
bool sensorError = false;
int tempDisplay = 0;
int currentDigit = 0;
const double TEMP_OFFSET = 0;

// --- LOGICA MEMORIE SI TIMER ---
const int EEPROM_ADDR[] = {0, 2};
int savedTemps[] = {0, 0};
unsigned long blinkUntil = 0;
bool displayVisible = true;
unsigned long lastActivityTime = 0;
const unsigned long TIMEOUT_OFF = 1800000;
bool isSystemOff = false;

EasyButton btn1(A1);
EasyButton btn2(A2);

// --- FUNCTII AUXILIARE ---
void triggerBlink() { blinkUntil = millis() + 500; }
void resetActivity() { lastActivityTime = millis(); }
void wakeSystem() { if (isSystemOff) isSystemOff = false; resetActivity(); }

double readSingleTemp() {
double val = thermocouple.readCelsius();
if (isnan(val) || val <= 0) { sensorError = true; return 0; }
sensorError = false;
return val + TEMP_OFFSET;
}

// CALLBACK BUTOANE
void onBtn1Short() { wakeSystem(); if (savedTemps[0] > 0) { Setpoint = savedTemps[0]; lastAdjustTime = millis(); triggerBlink(); } }
void onBtn1Long() { wakeSystem(); savedTemps[0] = (int)Setpoint; EEPROM.put(EEPROM_ADDR[0], savedTemps[0]); triggerBlink(); }
void onBtn2Short() { wakeSystem(); if (savedTemps[1] > 0) { Setpoint = savedTemps[1]; lastAdjustTime = millis(); triggerBlink(); } }
void onBtn2Long() { wakeSystem(); savedTemps[1] = (int)Setpoint; EEPROM.put(EEPROM_ADDR[1], savedTemps[1]); triggerBlink(); }

byte const digits[] = {
B00111111, B00000110, B01011011, B01001111, B01100110,
B01101101, B01111101, B00000111, B01111111, B01101111
};

void setup() {
// Serial.begin(9600); // Dezactivat pentru stabilitate, activează doar la nevoie
for (int i = 0; i < 7; i++) pinMode(segPins[i], OUTPUT);
for (int i = 0; i < 3; i++) pinMode(digitPins[i], OUTPUT);
pinMode(heaterPin, OUTPUT);
analogWrite(heaterPin, 0);

btn1.begin(); btn2.begin();
btn1.onPressed(onBtn1Short); btn1.onPressedFor(2000, onBtn1Long);
btn2.onPressed(onBtn2Short); btn2.onPressedFor(2000, onBtn2Long);

for (int i = 0; i < 2; i++) {
int val; EEPROM.get(EEPROM_ADDR[i], val);
if (val >= 150 && val <= 400) savedTemps[i] = val; else savedTemps[i] = 0;
}
myEnc.write(0);
resetActivity();
wdt_enable(WDTO_2S);
}

void loop() {
wdt_reset();
btn1.read();
btn2.read();

if (!isSystemOff && (millis() - lastActivityTime > TIMEOUT_OFF)) isSystemOff = true;

// Logică Encoder
long newPos = myEnc.read();
if (millis() - lastEncoderTime > 20) {
long deltaPos = newPos - oldPosition;
if (abs(deltaPos) >= 4) {
wakeSystem();
oldPosition = newPos;
lastEncoderTime = millis();
lastAdjustTime = millis();
if (deltaPos > 0) Setpoint += 5; else Setpoint -= 5;
Setpoint = constrain(Setpoint, 150, 400);
}
}

// --- CITIRE SENZOR ȘI CONTROL (Executate la 250ms) ---
if (millis() - lastUpdate > 250) {
lastUpdate = millis();
double currentRead = readSingleTemp();

// Filtru digital pentru netezire
if (Input == 0) Input = currentRead;
else Input = (Input * 0.7) + (currentRead * 0.3);

// --- LOGICĂ CONTROL "ULTIMATUM" (OPTIMIZATĂ PENTRU INERȚIE MARE) ---
if (sensorError || isSystemOff) {
pwm = 0;
} else {
float dif = Setpoint - Input;

// Dacă scade sub prag (chiar și 0.1 grade), dăm putere maximă imediat
if (dif > 0.1) {
pwm = 255;
}
// Putere de menținere pentru a "îndulci" coborârea inerțială
else if (dif > -0.8) {
pwm = 160;
}
// Oprim complet la Setpoint + 0.8 grade
else {
pwm = 0;
}
}
analogWrite(heaterPin, pwm);

/* // Monitorizare (Opțională)
Serial.print("SET:"); Serial.print(Setpoint);
Serial.print(" REAL:"); Serial.print(Input);
Serial.print(" PWM:"); Serial.println(pwm);
*/
}

// --- LOGICĂ AFIȘAJ ---
if (millis() < blinkUntil) displayVisible = (millis() / 100) % 2;
else displayVisible = true;

if (isSystemOff) {
tempDisplay = -1; // Va afișa "---"
} else if (sensorError) {
tempDisplay = -111; // Va afișa "Err"
} else if (millis() - lastAdjustTime < 2500) {
tempDisplay = (int)Setpoint;
} else {
// FILTRU VIZUAL: Dacă eroarea e mică (+/- 5.5 grade), afișăm fix Setpoint-ul
if (abs(Input - Setpoint) <= 5.5) {
tempDisplay = (int)Setpoint;
} else {
tempDisplay = (int)Input;
}
}

if (displayVisible) refreshDisplay(tempDisplay);
else clearDisplay();
}

void clearDisplay() { for (int i = 0; i < 3; i++) digitalWrite(digitPins[i], LOW); }

void refreshDisplay(int val) {
// Multiplexare ultra-rapida
digitalWrite(digitPins[currentDigit], LOW);
currentDigit = (currentDigit + 1) % 3;
byte segments = 0;

if (val == -1) { // OFF
segments = B01000000;
} else if (val == -111) { // ERR
if (currentDigit == 0) segments = B01111001; // E
else segments = B01010000; // r
} else {
int t = abs(val);
int d;
if (currentDigit == 0) d = (t / 100) % 10;
else if (currentDigit == 1) d = (t / 10) % 10;
else d = t % 10;

if (currentDigit == 0 && d == 0) segments = 0; // Stingere zero nesemnificativ
else segments = digits[d];
}

for (int i = 0; i < 7; i++) {
digitalWrite(segPins[i], (segments & (1 << i)) ? LOW : HIGH);
}
digitalWrite(digitPins[currentDigit], HIGH);
delayMicroseconds(800);
}