Va salut !
Va prezint statia mea de lipit cu Arduino. Realizata cu AI Gemini, updatata cu Vercel si Qwen ; 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 ; Qwen a sugerat un algoritm "predictiv" surprinzator de eficient .
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 !
Update : versiunea imbunatatita/updatata pe Elforum.

/*
//
// STATIE DE LIPIT CU ARDUINO
// DISPLAY LED CU 7 SEGMENTE
// ENCODER SI 2 MEMORII
// STAND-BY DUPA 30 MINUTE
// pt PENSOL SL-10
//
//
// mai multe info aici :
//
// www.elforum.info/topic/165506-statie-de-lipit-cu-arduino
*/
#include <max6675.h>
#include <Encoder.h>
#include <EEPROM.h>
#include <EasyButton.h>
#include <avr/wdt.h>
#include <avr/io.h>
#include <avr/interrupt.h>
// --- CONFIGURARE HARDWARE ---
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 errorCounter = 0;
const int MAX_ERROR_COUNT = 4;
float lastInput = 0;
float tempTrend = 0;
int pwm_hold_base = 150;
// --- LOGICA MEMORIE SI TIMER ---
const int EEPROM_ADDR[] = {0, 2};
int savedTemps[] = {0, 0};
unsigned long blinkUntil = 0;
unsigned long lastActivityTime = 0;
const unsigned long TIMEOUT_OFF = 1800000;
bool isSystemOff = false;
EasyButton btn1(A1);
EasyButton btn2(A2);
// --- VARIABILE PENTRU DISPLAY ---
volatile uint8_t isrCurrentDigit = 0;
volatile int isrDisplayValue = 0;
volatile bool isrDisplayEnabled = true;
// TABEL CIFRE
byte const digits[] = {
B00111111, B00000110, B01011011, B01001111, B01100110,
B01101101, B01111101, B00000111, B01111111, B01101111
};
// --- FUNCTII AUXILIARE ---
void triggerBlink() { blinkUntil = millis() + 500; }
void resetActivity() { lastActivityTime = millis(); }
void wakeSystem() { if (isSystemOff) isSystemOff = false; resetActivity(); }
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(); }
// --- INTERRUPT SERVICE ROUTINE (DISPLAY) ---
ISR(TIMER1_COMPA_vect) {
digitalWrite(digitPins[isrCurrentDigit], LOW);
isrCurrentDigit = (isrCurrentDigit + 1) % 3;
byte segments = 0;
if (!isrDisplayEnabled) {
segments = 0;
} else if (isrDisplayValue == -1) {
if (isrCurrentDigit == 0) segments = B01101101;
else if (isrCurrentDigit == 1) segments = B01111000;
else segments = B01111100;
} else if (isrDisplayValue == -111) {
if (isrCurrentDigit == 0) segments = B01111001;
else segments = B01010000;
} else {
int t = abs(isrDisplayValue);
int d = 0;
if (isrCurrentDigit == 0) d = (t / 100) % 10;
else if (isrCurrentDigit == 1) d = (t / 10) % 10;
else d = t % 10;
if (isrCurrentDigit == 0 && d == 0 && t < 100) segments = 0;
else segments = digits[d];
}
for (int i = 0; i < 7; i++) {
digitalWrite(segPins[i], (segments & (1 << i)) ? LOW : HIGH);
}
digitalWrite(digitPins[isrCurrentDigit], HIGH);
}
void setupTimer1() {
cli();
TCCR1A = 0; TCCR1B = 0; TCNT1 = 0;
OCR1A = 249;
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS11) | (1 << CS10);
TIMSK1 |= (1 << OCIE1A);
sei();
}
void setup() {
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);
setupTimer1();
}
void loop() {
wdt_reset();
btn1.read();
btn2.read();
if (!isSystemOff && (millis() - lastActivityTime > TIMEOUT_OFF)) isSystemOff = true;
// CONTROL 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 SI CONTROL INCALZIRE
if (millis() - lastUpdate > 250) {
lastUpdate = millis();
double currentRead = thermocouple.readCelsius();
if (isnan(currentRead) || currentRead <= 0 || currentRead > 500) {
errorCounter++;
if (errorCounter >= MAX_ERROR_COUNT) { sensorError = true; pwm = 0; }
} else {
errorCounter = 0;
sensorError = false;
Input = (Input == 0) ? currentRead : (Input * 0.8) + (currentRead * 0.2);
float instantTrend = (Input - lastInput) / 0.25;
tempTrend = (tempTrend * 0.6) + (instantTrend * 0.4);
lastInput = Input;
if (isSystemOff) {
pwm = 0;
} else {
float dif = Setpoint - Input;
if (dif > 1.5) pwm = 255;
else if (dif > -1) pwm = pwm_hold_base + (tempTrend < 0 ? (int)(abs(tempTrend) * 30) : 0);
else pwm = 0;
}
}
analogWrite(heaterPin, constrain(pwm, 0, 255));
}
// --- LOGICA DE AFISARE ---
int valToDisplay = 0;
if (isSystemOff) {
valToDisplay = -1;
} else if (sensorError) {
valToDisplay = -111;
} else if (millis() - lastAdjustTime < 2000) {
valToDisplay = (int)Setpoint; // Afisam setpoint-ul in timp ce reglam
} else {
// +/- 5 grade fata de Setpoint
if (abs(Input - Setpoint) < 5.1) {
valToDisplay = (int)Setpoint;
} else {
valToDisplay = (int)(Input + 0.5); // Rotunjire corecta
}
}
noInterrupts();
isrDisplayEnabled = (millis() < blinkUntil) ? ((millis() / 100) % 2) : true;
isrDisplayValue = valToDisplay;
interrupts();
}

Niciun comentariu:
Trimiteți un comentariu