Hier kommen ja doch auch häufiger mal Ofen und Hochtemperaturthemen auf und da mich das Thema selbst brennend (SCNR ) interessiert und auch Projekte in die Richtung immer wieder vorkommen mach ich mal nen Sammelfaden.
Konkret fertig hab ich grade ein dreikanaliges Thermometer, das von 0° bis 1000°C misst, und die Werte über serielle Schnittstelle oder SD-Karte loggen kann. Ist für den Pizzaofen. So sieht das aus:
Der Ofen hat schon eine dafür vorgesehene Mulde, ich brauche also nur noch ne Frontplatte dafür machen. Wie genau ich das löse bin ich mir noch nicht ganz sicher, weil das ja ganzjährig draußen ist, und ich die Ekeltronik gerne möglichst gut schützen möchte. An der Stelle wo das Thermometer nachher sitzt ist aber kein Platz, nen Regenschild o.ä. anzubringen. Muss ich noch n bisschen dran rumhirnen...
Auf den ersten drei 4er-Blöcken werden die drei Thermometer angezeigt, der vierte hat zur Wahl entweder die Uhrzeit, oder die Position eines Faders, mit dem ich behenfsmäßig den Rauchschieber mit in die Log-Datei reinschummle. muss halt dran denken, immer den Fader zu bewegen, wenn ich was am Rauchschieber mache. Der Fader ist grad noch nicht verbaut, der Code liest ihn aber schon ein.
Die Messwandler könnten wohl noch ne Spannungsstabilisierung brauchen, da ist etwas Rauschen, das hab ich mit ner Mittelwertberechnung rausgebügelt. Für den Pizzaofen gut genug.
Loggingintervall ist einstellbar zwischen 1 und 9999 Sekunden. Da müssen noch Edgecases abgefangen werden, und bis auf 9999 Sekunden in Einerschritten den Encoder hochzukurbeln macht auch sicher keinen Spaß.
Kurze Bedienungsanleitung steht im Code.
Es braucht an Hardware:
1x Arduino Mega
1x RTC DS1307 (ich würde jetzt die ds3132 nehmen, weil da n über i2c beschreibbares EEPROM drauf ist, aber jetzt hab ich das verbaut. Für den ds1307 gibts aber ne DummyLib, die die Zeit dann aus Millis() oder so ableitet, die sollte auch genau genug sein)
1x µSD-Kartenleser mit Levelshifter für 5V Host
2x 8fach siebensegmentanzeigen an Max7219, kaskadiert
3x Max6675 für die Typ K Thermoelemente
3x Typ-K Thermoelement (d'uh)
1x Taster
1x Encoder
N Netzteil, das aus 12V Wackelsaft die 7V= für den Arduino macht gibts auch noch, aber das brauchts hier ja grad nicht.
und hier ist der Kot:
Code: Alles auswählen
/*-----------------------------------------------
* 3-Kanaliges Thermometer mit Loggingfunktion
* ----------------------------------------------
*
* Getestet auf Arduino Mega pro mini
* Braucht 3x Max6675 für die Typ_K Sensoren, eine DS1307 für die Uhrzeiten und einen SD-Slot mit Levelshifter für 5V->3,3v An SPI
* SD-Karte mit bevorzugt FAT16 (32 soll auch gehen, ungetestet)
* Außerdem einen Encoder und einen weiteren Eingabetaster für die Bedienung.
* Angezeigt wird über zwei in Serie gehängte 8x 7-Segment Anzeigen an Max7219
*
* ---
*
* Der Sketch geht davon aus, dass die RTC keine Batterie hat und verlangt daher nach jedem Powercycle ein Neueinstellen von Datum und Uhrzeit.
* Dies geschieht über den Encoder, mit Druck auf den Encoder kommt man ins nächste Feld.
* Mit Button1 kommt man ein Feld zurück, wenn man aus dem ersten Feld Button1 drückt, kann man die Zeiteinstellung überspringen.
* Die Dezimalpunkte zeigen an, welches Feld gerade aktiv ist.
* Die Reihenfolge ist: Tag - Monat - Jahr - Stunden - Minuten - Intervall
*
* Mit Intervall lässt sich die Häufigkeit des Loggings einstellen, per Default ist sie auf 30s festgelegt.
*
* Um Das Logging zu aktivieren, muss man länger als 8 Sekunden Button1 gedrückt halten. währenddessen wird über die Dezimalpunkte im Display angezeigt,
* wie lange man den Knopf noch gedrückt halten muss. Bei aktiviertem Logging machen die Dezimalpunkte ein schnelles Lauflicht im entgegengesetzten Sinn.
* Logging abschalten wie anschalten.
*
* Das Loggingintervall (und auch die Zeit) kann auch im Betrieb geändert werden, dazu braucht es einen langen Druch auf den Encoder. (8Sekunden)
* Dabei läuft ein langsames Lauflicht rückwärts.
* ->Änderung funktioniert nur bei abgeschaltetem Logging!
*
*/
//----------------------------------------------
#include "RTClib.h"
RTC_DS1307 rtc;
#include <Encoder.h>
Encoder myEnc(19, 18);
#include "max6675.h"
#include <Adafruit_Sensor.h>
#include "LedControl.h"
//Sd-Karte
#include <SD.h>
File dataLog;
const int chipSelect = 53; //Wg Mega, sonst 10
bool sd_ok = 0;
/*
pin 12 is connected to the DataIn
pin 11 is connected to the CLK
pin 10 is connected to LOAD
We have two MAX7219.
*/
LedControl lc=LedControl(24,26,25,2);
long oldPosition = 0; //encoder Var
// Max6675_1
const byte thermo1D0 = 2; // so
const byte thermo1CS = 3;
const byte thermo1CLK = 4; // sck
unsigned int temp1 = 0;
MAX6675 thermocouple1(thermo1CLK, thermo1CS, thermo1D0);
// Max6675_2
const byte thermo2D0 = 5; // so
const byte thermo2CS = 6;
const byte thermo2CLK = 7; // sck
unsigned int temp2 = 0;
MAX6675 thermocouple2(thermo2CLK, thermo2CS, thermo2D0);
// Max6675_3
const byte thermo3D0 = 8; // so
const byte thermo3CS = 9;
const byte thermo3CLK = 10; // sck
unsigned int temp3 = 0;
MAX6675 thermocouple3(thermo3CLK, thermo3CS, thermo3D0);
//Fader
unsigned long FadeWert=0; //DummyWert für späteres Upgrade mit Feuchtigkeitssensor
int FadewAlt=0;
//IO
const byte EButtonPin=22; //Encoder Button
const byte Button1Pin=23; //Button 1
const byte wasistderPin=A15; //Fader-Pin für Erfassung der Rauchklappenposition
//Var
bool EncButton=true;
bool EncButtonpressed;
bool Button1=true;
bool Button1pressed=false;
unsigned long logTimer=0; //für Logginintervallmessung
unsigned long DebTimer=0; //für Button-Debounce
unsigned long PressTimer=0; //für Lange Drücker
byte DpN=0; //für Anzeige von Dezimalpunkten (außer Datums- und Uhrzeiteinstellung)
int Jahr=2020; //Für Zeit einstellen/schreiben/lesen/loggen
byte Monat=10;
byte Tag=24;
byte Stunde=10;
byte Minute=0;
byte Sekunde=0;
byte EncTarget=1; //Wohin zielt der Encoder, außerdem: wird gerade die Zeit eingestellt?
//bool ZeitSetzen=true;
bool LoggingOn=false; //wird gerade geloggt?
unsigned long Intervall= 30; //Logging intervall, (30 Sekunden) (wird mit 1000 multipliziert und braucht daher einen größeren Zahlenraum)
byte kurzAlt=0; //Hilfsvariable zur verkürzung des Intervalls Logintervall(30s) zu Mess- und Anzeigeintervall(1s)
bool f4umschalt=false; //displayfeld 4 umschalten zwischen Uhr und Feuchtigkeit(sensor noch nicht da)
//-------------------------
void setup(){
Serial.begin(2000000);
pinMode(Button1Pin, INPUT_PULLUP);
/*
The MAX72XX is in power-saving mode on startup,
we have to do a wakeup call
*/
lc.shutdown(0,false); //Display 1, display 2, usw.
lc.shutdown(1,false);
/* Set the brightness to a medium values */
lc.setIntensity(0,1);
lc.setIntensity(1,1);
/* and clear the display */
lc.clearDisplay(0);
lc.clearDisplay(1);
//---RTC---
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
abort();
}
if (! rtc.isrunning()) {
Serial.println("RTC is NOT running, let's set the time!");
// When time needs to be set on a new device, or after a power loss, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
// rtc.adjust(DateTime(Jahr, Monat, Tag, Stunden, Minuten, 0));
}
// für DS logging: open serial communications and wait for port to open:
Serial.print("Initialisiere SD Karte...");
// initialize the SD card
if ( !SD.begin(53) )
Serial.println("Initialisierung fehlgeschlagen!"); // initialization error
else{ // initialization OK
sd_ok = 1;
Serial.println("Initialisierung erledigt.");
//if( SD.exists("Log.txt") == 0 ){ // test if file with name 'Log.txt' already exists
// create a text file named 'Log.txt'
Serial.print("\r\nerzeuge 'Log.txt' Datei ... ");
dataLog = SD.open("Log.txt", FILE_WRITE); // create (&open) file Log.txt
if(dataLog) { // if the file opened okay, write to it:
Serial.println("OK");
// write some texts to 'Log.txt' file
dataLog.println(" Datum | Uhrzeit | TEMP1 | TEMP2 | TEMP3 | KLAPPE ");
dataLog.println("(dd-mm-yyyy)|(hh:mm:ss)|");
dataLog.close(); // close the file
}
/*else
Serial.println("error creating file.");
}*/
}
Serial.println("\r\n Datum | Uhrzeit | TEMP1 | TEMP2 | TEMP3 | KLAPPE ");
Serial.println("(dd-mm-yyyy)|(hh:mm:ss)|");
callRTC(); //für weniger Einstellaufwand beim testen
if(!sd_ok){
noSDWarnung(); //Display Errormessage
}
//Schnitt initialisieren
temp1 = 8*thermocouple1.readCelsius() ; //einlesen und multiplizieren, Ausgangsposition für schnittbildung
temp2 = 8*thermocouple2.readCelsius();
temp3 = 8*thermocouple3.readCelsius();
FadeWert= 8*analogRead(wasistderPin);
}
// --- ende setup ---
void noSDWarnung(){
//Warnung wenn keine SD-Karte da ist
for (int i = 0; i <= 8; i++) {
write4ValTo7Segment( 8888, 8888, 8888, 8888, false, false, false, false, false, false, false, false);
delay(125);
lc.clearDisplay(0); //nichts anzeigen
lc.clearDisplay(1);
delay(125);
lc.setChar(0,0,'-',false); //Max7219(0), Bindestriche anzeigen
lc.setChar(0,1,'-',false);
lc.setChar(0,2,'-',false);
lc.setChar(0,3,'-',false);
lc.setChar(0,4,'-',false);
lc.setChar(0,5,'-',false);
lc.setChar(0,6,'-',false);
lc.setChar(0,7,'-',false);
lc.setChar(1,0,'-',false); //Max7219(1)
lc.setChar(1,1,'-',false);
lc.setChar(1,2,'-',false);
lc.setChar(1,3,'-',false);
lc.setChar(1,4,'-',false);
lc.setChar(1,5,'-',false);
lc.setChar(1,6,'-',false);
lc.setChar(1,7,'-',false);
delay(125);
lc.clearDisplay(0); //nichts anzeigen
lc.clearDisplay(1);
delay(125);
}
}
void logSD(){
char buffer1[34], buffer2[26];
sprintf(buffer1, "%04u°C | %04u°C | %04u°C | %04umm ", temp1/8, temp2/8, temp3/8, FadewAlt/8);
sprintf( buffer2, " %02u-%02u-%04u | %02u:%02u:%02u | ", Tag, Monat, Jahr,
Stunde, Minute, Sekunde);
// print data on PC serial monitor
Serial.print(buffer2);
Serial.println(buffer1);
// write data to SD card
if(sd_ok){
// if the SD card was successfully initialized
// open Log.txt file with write permission
dataLog = SD.open("Log.txt", FILE_WRITE);
dataLog.print( buffer2 ); //Zeit
dataLog.println( buffer1 ); //Temps
dataLog.close(); // close the file */
}
}
void Enc(){
int newPosition = myEnc.read()/4 ; //Liest pro Rastung 4 neue Werte ein, daher /4
if (newPosition != oldPosition) {
int AdVal=(oldPosition-newPosition);//Serial.println(AdVal);
oldPosition=newPosition;
switch (EncTarget) { //Was soll mit dem Encoder gerade eingestellt werden?
case 1:
Tag=Tag+AdVal; //Tage einstellen
Tag=constrain(Tag, 1,31);
break;
case 2:
Monat=Monat+AdVal; //Monate einstellen
Monat=constrain(Monat, 1,12);
break;
case 3:
Jahr=Jahr+AdVal; //Jahre einstellen
Jahr=constrain(Jahr, 2020, 3000);
break;
case 4:
Stunde=Stunde+AdVal; //Stunde einstellen
Stunde=constrain(Stunde, 0,23); //compiler meckert, weil byte eh nie kleiner 0 wird. EGAL!
break;
case 5:
Minute=Minute+AdVal; //Minute einstellen
Minute=constrain(Minute, 0,59); //compiler meckert, weil byte eh nie kleiner 0 wird. EGAL!
break;
case 6:
if(Intervall<10){ //Loggingintervall einstellen
Intervall=Intervall+AdVal;
goto escape;
}
if(Intervall>9 &&Intervall<60){ //von 10-60 +- 10 Sekunden
Intervall=Intervall+AdVal*10;
goto escape;
}
if(Intervall>59 &&Intervall<9999){ //von 60-3600 +- 60 Sekunden
Intervall=Intervall+AdVal*60;
goto escape;
}
escape:
Intervall=constrain(Intervall, 1, 9960);
break;
}
}
}
void ButtonLogic(){
//einlesen
EncButton=digitalRead(EButtonPin);
Button1=digitalRead(Button1Pin);
//verlogeln
if(EncTarget){ //on Startup, da (noch/dauerhaft?) keine Batterie
if(!EncButton&&millis()-DebTimer>350){
EncTarget++;
if(EncTarget>6){
rtc.adjust(DateTime(Jahr, Monat, Tag, Stunde, Minute, 0));
// Loggingintervall aus SD-Karte schreiben
dataLog = SD.open("Log.txt", FILE_WRITE); // create (&open) file Log.txt
dataLog.print("Geloggt wird alle ");
dataLog.print(Intervall);
dataLog.println(" Sekunden, aktuelles Datum: ");
dataLog.close(); // close the file
// Loggingintervall über Serielle Schnittstelle bekanntgeben
Serial.print("Geloggt wird alle ");
Serial.print(Intervall);
Serial.println(" Sekunden, aktuelles Datum: ");
logSD(); //um das aktuelle Datum zu schreiben, ein einziges mal Log() ausführen.
EncTarget=0; //Einstellen fertig
}
DebTimer=millis();
}
if(!Button1&& millis()-DebTimer>500 && EncTarget>0){ //träger Taster, braucht Zeit zum Drücken, deswegen 500ms
EncTarget--;
DebTimer=millis();
}
}
if(!EncTarget){ //Umschalten zw Uhrzeit- u. Faderanzeuge in Feld 4
if(!EncButton&&millis()-DebTimer>350){
f4umschalt=!f4umschalt;
DebTimer=millis();
}
//---
//Langer Druck Button1 -> Logging an- und aussschalten
if(!Button1&&!Button1pressed){ //wird gedrückt, timer setzen
Button1pressed=true;
PressTimer=millis(); //8s halten bis Beginn/Ende
}
if(!Button1&&Button1pressed&& millis()-PressTimer<8000){ //wird gehalten, DpN visualisieren
DpN=(millis()-PressTimer)/1000+1; //Visualisierung Restzeit bis Loggingbeginn über Dezimalstellen
}
if(Button1&& Button1pressed&& millis()-PressTimer>7999){ //wird losgelassen, Aufgehts!
LoggingOn=!LoggingOn;
if(LoggingOn){
Serial.println(" "); //Formatierung-Leerzeile, unten weiter im Einsatz
Serial.println("Beginne Logging ...");
if(!sd_ok){
Serial.println("Warnung: SD-Karte fehlt/nicht erkannt!");
noSDWarnung(); //
}
dataLog = SD.open("Log.txt", FILE_WRITE); // create (&open) file Log.txt
dataLog.println(" ");
dataLog.println("Beginne Logging ...");
dataLog.close(); // close the file
}
if(!LoggingOn){
Serial.println("... Logging beendet.");
Serial.println(" ");
dataLog = SD.open("Log.txt", FILE_WRITE); // create (&open) file Log.txt
dataLog.println("... Logging beendet.");
dataLog.println(" ");
dataLog.close(); // close the file
}
}
if(Button1&&Button1pressed){
Button1pressed=false;
DpN=0;
}
//----Lange Drücke auf EncButton -------------------
if(!LoggingOn&&!EncButton&&!EncButtonpressed&&!EncTarget){ //wird gedrückt, timer setzen, Logging muss vorher deaktiviert werden
EncButtonpressed=true;
PressTimer=millis(); //8s halten bis Beginn/Ende
}
if(!EncButton&&EncButtonpressed&&millis()-PressTimer<8000){ //wird gehalten, DpN visualisieren
DpN=7-(millis()-PressTimer)/1000+1; //Visualisierung Restzeit bis Loggingbeginn über Dezimalpunkte
}
if(EncButton&& EncButtonpressed&& millis()-PressTimer>7999){ //wird losgelassen, Aufgehts!
EncTarget=6;
}
if(EncButton&&EncButtonpressed){
EncButtonpressed=false;
DpN=0;
}
//---------------------------------------
}
}
void callRTC() {
DateTime now = rtc.now();
Tag=(now.day());
Monat=(now.month());
Jahr=(now.year());
Stunde=(now.hour());
Minute=(now.minute());
Sekunde=(now.second());
}
void readTemps(){
byte Faktor=7; //schnitt vs aktuell
int t1neu=8*thermocouple1.readCelsius(); //einlesen
int t2neu=8*thermocouple2.readCelsius();
int t3neu=8*thermocouple3.readCelsius();
temp1 = ((temp1*Faktor)+t1neu) /(Faktor+1) ; //Schnitt bilden
temp2 = ((temp2*Faktor)+t2neu) /(Faktor+1) ; //Schnitt bilden
temp3 = ((temp3*Faktor)+t3neu) /(Faktor+1) ; //Schnitt bilden
}
void write4ValTo7Segment( int n1, int n2, int n3, int n4, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8){
// Für 2 gechainte max7219 mit 2x8 Siebensegment Anzeigen
int n;
n1 = abs(n1);
n2 = abs(n2);
n3 = abs(n3);
n4 = abs(n4);
/*bool b;
Dezimalpunkte sind paarig zusammengeschaltet */
//max7219_0
n=n2;
lc.setDigit(0, 0, n%10, b4); n/=10; //!Reihenfolge beachten n=n# vertauscht, damit Displays von links nach rechts angeordnet sind, dito b#
lc.setDigit(0, 1, n%10, b4); n/=10;
lc.setDigit(0, 2, n%10, b3); n/=10;
lc.setDigit(0, 3, n%10, b3);
n=n1;
lc.setDigit(0, 4, n%10, b2); n/=10;
lc.setDigit(0, 5, n%10, b2); n/=10;
lc.setDigit(0, 6, n%10, b1); n/=10;
lc.setDigit(0, 7, n%10, b1);
//max7219_1
n=n4;
lc.setDigit(1, 0, n%10, b8); n/=10;
lc.setDigit(1, 1, n%10, b8); n/=10;
lc.setDigit(1, 2, n%10, b7); n/=10;
lc.setDigit(1, 3, n%10, b7);
n=n3;
lc.setDigit(1, 4, n%10, b6); n/=10;
lc.setDigit(1, 5, n%10, b6); n/=10;
lc.setDigit(1, 6, n%10, b5); n/=10;
lc.setDigit(1, 7, n%10, b5);
}
//-----loop------
void loop(){
//Wird dauernd Gebraucht:
Enc(); //Encoder einlesen
ButtonLogic(); //Knöpfe einlesen und Programmablauf
int RhTumbN=analogRead(wasistderPin); //Fader einlesen
RhTumbN=8*RhTumbN;
FadeWert= ((FadeWert*13)+RhTumbN)/14 ; //Fader Schnitt bilden*/
if((abs(FadeWert-FadewAlt))>40){ //nur Änderungen >4 anzeigen, aber werte sind verachtfacht um trunkierung beim Schnittbilden zu entschärfen
FadewAlt=FadeWert;
}
//zu Beginn ist Zeit noch nicht gesetzt, Einzustellendes Datum und Intervall werden angezeigt
if(EncTarget){
// Zahlen1 Zahlen2 Zahlen3 Zahlen4 Punkt1(Tage) Punkt2(Monat) Punkt3(Jahr) Punkt4(Jahr) Punkt5(Stunde) Punkt6(Minute) Punkt7 + Punkt8(Intervall)
write4ValTo7Segment((100*Tag+Monat),Jahr, (Stunde*100+Minute), Intervall, !(-1+EncTarget), !(-2+EncTarget), !(-3+EncTarget), !(-3+EncTarget), !(-4+EncTarget), !(-5+EncTarget), !(-6+EncTarget), !(-6+EncTarget) );
}
//nur wenn Zeit schon gesetzt ist, Temperaturen und Zeiten werden eingelesen und angezeigt, wenn erwünscht wird geloggt
if(!EncTarget){
//Logging ist an
if(LoggingOn){
if(millis()-logTimer>Intervall*1000){
logSD(); //Logzeilen raus nach SD & serial
logTimer=millis();
}
if(millis()-PressTimer >100&&!Button1pressed){ //Signalisierung: Logging läuft! PressTimer wird recycelt
DpN++;
if(DpN>8){DpN=1;} //Lauflicht-Reset
PressTimer=millis();
}
byte kurzNeu=(millis()-logTimer)/1000; //Temperaturen&Zeit sollen häufiger eingelesen und angezeigt werden, als Loggingintervall (1x/Sekunde)
if (kurzNeu!=kurzAlt){ //falls (millis()-logTimer)/1000 zu groß für byte ist, läuft byte halt über ist egal, weil nur die Veränderung wichtig ist für erfassung von Wechseln
readTemps();
callRTC();
kurzAlt=kurzNeu;
}
}
//Logging ist aus, trotzdem Zeit und Temperaturen messen
if(!LoggingOn && millis()-logTimer>1000){
readTemps();
callRTC();
logTimer=millis(); //es wird nicht geloggt, LogTimer kann also wiederverwendet werden, 1x /Sekunde
}
//Display beschicken
int feld4;
if (!f4umschalt){
feld4=Stunde*100+Minute; //Zeitanzeige
}
if (f4umschalt){
feld4=FadewAlt/8; //für Drosselklappenstgellung FeuchteSensor
}
write4ValTo7Segment(temp1/8,temp2/8, temp3/8,(feld4), !(-8+DpN), !(-7+DpN), !(-6+DpN), !(-5+DpN), !(-4+DpN), !(-3+DpN), !(-2+DpN), !(-1+DpN));
}
}
Code: Alles auswählen
DATE | TIME | TEMP1 | TEMP2 | TEMP3
(dd-mm-yyyy)|(hh:mm:ss)|
Geloggt wird alle30 Sekunden
17-10-2020 | 14:14:19 | 0023°C | 0047°C | 0043°C | 9999X
17-10-2020 | 14:14:49 | 0023°C | 0047°C | 0043°C | 9999X
17-10-2020 | 14:15:19 | 0023°C | 0047°C | 0043°C | 9999X
17-10-2020 | 14:15:49 | 0023°C | 0047°C | 0042°C | 9999X
17-10-2020 | 14:16:19 | 0023°C | 0047°C | 0043°C | 9999X
17-10-2020 | 14:16:49 | 0023°C | 0047°C | 0043°C | 9999X
17-10-2020 | 14:17:19 | 0023°C | 0047°C | 0043°C | 9999X
Als nächstes kommt ne Ofensteuerung für Keramikbrände, hier wurde ja schon mal an dem Thema gearbeitet und über PID-Regelung nachgedacht. Hab heute mit RMK drüber PMs geschrieben und er ist auch schon Feuer und Flamme.
Ich glaub PID würd ich der Einfachheit erstmal weglassen und fix mit ner Hysterese von 14°K arbeiten, bei 7 unter Soll ist Heizungsbeginn, bei 7 drüber schaltet er wieder aus und von vorne. Dann kann ich mir erstmal Gedanken über die Heizkurvenberechnung machen, und wenn PID dann noch relevante Vorteile verspricht, (weniger Schaltspiele, größere Genauigkeit) kann man sich das ja immernoch "antun".
Problem beim Brennen von Keramiken ist ja, dass man bis zum Quarzsprung langsam (~85°K/h) heizen muss, weil einem das Zeug sonst futsch geht.
Für Keramikbrände möchte man eigentlich auch nochmal n besseres Thermoelement, ich glaub die heißen Typ-S, ich fürchte, das braucht dann auch andere Messwandler, der max6675 kann nur Typ-K.
Grüße
Moritz
edit: paar kleine Codeverbesserungen