Arduino Variable

Antworten
christian_p
Posting Klettermax
Posting Klettermax
Beiträge: 231
Registriert: Dienstag 4. April 2006, 21:22

Arduino Variable

#1

Beitrag von christian_p »

Hallo zusammen,
nachdem sich hier einige Profis auf diesem Bereich tummeln, melde ich mich mal mit meinem Anliegen.

Ich automatisiere seit einiger Zeit mein Sudhaus und wie das halt so ist, gibt es immer etwas zum verbessern.
Grundsätzlich funktioniert schon alles, allerdings würde ich es gerne ein wenig kompakter programmieren...einfach um noch ein wenig dazuzulernen.

Mein Anliegen:

Ich speichere für meine Rasttemperaturen unter den Variablen T1, T2, T3, T4 und T5 die Temperaturen sowie Z1, Z2, Z3, Z4 und Z5 die Zeiten für verschiedene Rasten.
Aktuell habe ich eine eigene if-Funktion, für jede Rast. Abgekürzt in etwa so:

int T1 = EEPROM.read(11);
int T2 = EEPROM.read(12);
int T3 = EEPROM.read(13);
..

if (HeizenRast1 == 1{
TSoll=T1;
...
if (Tsoll>Tist{
Heizung=ein;
}
if Tsoll<=Tist{
Rast=1;
Rastzeit = Z1
}
if (Rastzeit = "abgelaufen"){
HeizenRast1=0;
HeizenRast2=1;
}

Natürlich ist diese if-Abfrage deutlich umfangreicher, für die Übersicht jedoch abgekürzt

Was ich mir vorstelle, wäre nur eine Funktion für alle Rasten, bei der sich der Index (1, 2, 3, 4, 5) der Temperaturen (T1, T2...) bei Ablauf einer Rast um eins erhöht. Das Programm steht soweit, nur beisse ich mir aktuell noch die Zähne daran aus, wie man das mit dem "Index realisiert.
Gibt es die Möglichkeit, es in etwa so zu realisieren.

Zahl = 1;

if (Aufheizen = Ein){
TSoll = T[Zahl]; // Zahl ist 1 oder 2 oder 3...
}
...
if (Rastzeit = "abgelaufen"){
Zahl++;
}

Diese hier genannten Ausschnitte sind so sicher nicht funktionsfähig, ich möchte damit nur erklären, was ich machen möchte.
Ich bin mir sicher, meine Herangehensweise ist etwas unorthodox und es gibt auch sicher bessere Programme, welche bereits fertig sind...es gibt aber auch gutes Bier zu kaufen, trotzdem braue ich selber.

Ich würde mich über Tipps freuen, wie man diese "Index"-Übergabe realisieren kann...brauche nicht unbedingt eine fertige Lösung.
Gruß
Christian
JackFrost
Posting Freak
Posting Freak
Beiträge: 2984
Registriert: Dienstag 15. Mai 2018, 18:10

Re: Arduino Variable

#2

Beitrag von JackFrost »

Hi,

Das wichtigste ist das du es so schreibst das du in einem Jahr deinen Code noch debuggen kannst.

Dann nicht blockierend schreiben.

Du kannst das mit einem Array schon machen, schreib es dir aber nicht zu kompliziert.

Ich hab bei meiner Steuerung alle Rezept Parameter in einem Struct und hole die aus dem FRAM ähnlich wie bei dir.

Die einzelnen Rasten arbeite ich in einem Switch case ab.
Das gibt dann kein so großes id/else Konstrukt.
Ist noch leicht erweiterbar.

Die Rasten laufen über einen Status. Und mit defines kann ich das sehr schnell erweitern.

Ich kann dir heute Abend mal einen Auszug machen, dann ist es leichter zu sehen was ich meine.

Gruß JackFrost
Meine Hardware:
eManometer
IDS2 ohne CBPi
Magnetrührer
Ss-Brewtech 10 Gal Topf
IDS2 Induktionsplatte
christian_p
Posting Klettermax
Posting Klettermax
Beiträge: 231
Registriert: Dienstag 4. April 2006, 21:22

Re: Arduino Variable

#3

Beitrag von christian_p »

Hallo Jack, vielen Dank für die Info.
Ich habe das Maischprogramm in einem eigenen void.

Es geht mir aktuell nur um das Verständnis der Übergabe der Parameter. Ich schaue mir heute Abend Mal deine Vorschläge an, gerne kannst du noch den Ausschnitt Posten
Gruß
Christian
JackFrost
Posting Freak
Posting Freak
Beiträge: 2984
Registriert: Dienstag 15. Mai 2018, 18:10

Re: Arduino Variable

#4

Beitrag von JackFrost »

Hi,

ich nutze in meiner Steuerung für die IDS2 den folgenden Code für die Rasten. Das ganze ist als Zustandsautomat der je nach Status und Bedingung weiterspring. Alles bisserl Denglisch, das muss ich noch verbessern, so das es einheitlich ist.

Als erstes hab ich Definitionen angelegt, damit ich das schnell um eine Stufe oder Rast erweitern kann ohne das ich an vielen Stellen was drehen muss :

Code: Alles auswählen

#define number_of_rasts 7
#define number_of_hop_timers 7
#define number_of_hop_flame_out_timers 2

#define status_mash_start 0
#define status_rast_0 1
#define status_rast_1 2
#define status_rast_2 3
#define status_rast_3 4
#define status_rast_4 5
#define status_rast_5 6
#define status_rast_6 7
#define status_jodnormal 8
Dann hab ich mir einen Struct mit dem ganzen Rezept angelegt, Damit hab ich alles beisammen.

Code: Alles auswählen

typedef struct Recipe
{
	uint8_t ID;
	float temperatur_mash;
	float rast_temp[number_of_rasts];
	uint16_t rast_time[number_of_rasts];
	uint8_t dekoktion[number_of_rasts];
	uint16_t hop_time[number_of_hop_timers];
	uint16_t hop_time_flame_out[number_of_hop_flame_out_timers];
	float hop_temp_flame_out[number_of_hop_flame_out_timers];
	uint8_t cooler;
	uint8_t Irish_moos;
	uint8_t yeast;
	float cooling_temp;
	uint16_t boil_time;
	float lauter_temp;
	uint32_t crc;
} Recipe_t, *p_Recipe_t;
Für dich wichtig ist der Punkt der Rasten und die Zeiten dazu :

Code: Alles auswählen

float rast_temp[number_of_rasts];
uint16_t rast_time[number_of_rasts];
Die sind als Arrays angelegt.

Ich hab eine Variable für den Typ Recipe_t und einen Zeiger auf diese Variable angelegt. Ich arbeite nur mit dem Zeiger. Für alle
Übergaben nutze ich den Zeiger, das spart Speicher, da nur die Bytes vom Zeiger übergeben werden und nicht der ganze Block. Zudem muss beim Ändern nichts zurück geschrieben werden, da hier direkt im Struct gerarbeitet wird. Die Daten vom FRAM oder EEPROM hole ich Byteweise und speicher sie an der Adresse auf die der Zeiger zeigt. Damit hab ich den ganzen Block auf einmal. Zudem wird automatisch die Länge des Struct bestimmt. Wenn ich was ändere muss ich mich hier auch um nichts kümmern. Das Arbeiten mit Zeigern klingt erstmal komisch bringt aber viele Vorteile und kann deutlich RAM sparen wenn es knapp wird.

Code: Alles auswählen

Recipe_t recipe;
p_Recipe_t p_recipe = &recipe;
Es gibt noch eine Variable die den aktuellen Status des Automodes beinhaltet, das ist ein einfacher uint8_t. 1 Byte reicht hier.
Über ein Switch Case wird dann alles abgefragt. Das spart viele If und ist mit Copy und Paste schnell erweiterbar wenn ich merke 8 Rasten wären besser.

Code: Alles auswählen

switch(status_automode)
			{
				case status_mash_start:
				{
					if(check_rast)
					{
						setpoint =  p_recipe->temperatur_mash;
						check_rast = false;
					}
					if(get_key_long( PORT_PA05))
					{
						if(!mash_in_running)
						{
							mash_in_running = true;
						}
						else
						{
							overide_status = true;
						}
						
						
						
					}
					if(!wait_temp_drop && !overide_status)
					{
						if(delta >= -0.5 && delta <= 0.5  )
						{
							wait_temp_drop = true;
							Buzzer_trigger |= mash_buzzer;
							blink_overide_green = true;
							//if(!(p_system_->skip_pH%2) && automode_time == 0)
							//{
								//pH_timer = p_system_->pH_delay;
							//}
						}
					}
					else
					{
						if((delta >= 2 && auto_mash_mode ) || overide_status)
						{
							if(!(p_system_->skip_pH%2))
							{
								pH_timer = p_system_->pH_delay;
								run_pH_timer = true;
							}
							p_documentation_data->mash_time = timestamp;
							overide_status = false;
							blink_overide_green = false;
							wait_temp_drop = false;
							status_automode++;
							mash_in_running = false;
							if(p_recipe->dekoktion[status_automode-1]%2)
							{
								dekoktion = true;
								dekoktion_setpoint = p_recipe->temperatur_mash;
							}
							else
							{
								dekoktion = false;
							}
							automode_time = p_recipe->rast_time[status_automode-1];
							check_rast = true;
							run_timer = false;
						}
					}
					
				}
				break;
				case status_rast_0:
				{
					if(get_key_short(PORT_PA05))
					{
						if(!pause)
						{
							pause = true;
						}
						else
						{
							pause = false;
						}
					}
					if(check_rast)
					{
						if(p_recipe->rast_temp[status_automode-1] > 25.0)
						{
							check_rast = false;
							last_rast = status_automode-1;
							min_temp = p_recipe->rast_temp[status_automode-1];
							max_temp = p_recipe->rast_temp[status_automode-1];
							setpoint =  p_recipe->rast_temp[status_automode-1];
							delta = setpoint - temp;
						}
						else
						{
							status_automode++;
							automode_time = p_recipe->rast_time[status_automode-1];
							check_rast = true;
						}
					}
					if(delta <= 0.5 && delta >= -0.5 && gradient > -0.1)
					{
						run_timer = true;
						dekoktion = false;
					}
					if(run_timer)
					{
						if(min_temp > temp)
							min_temp = temp;
						if(max_temp < temp)
							max_temp = temp;
					}
					if(automode_time == 0)
					{
						p_documentation_data->rast_time[status_automode-1] = timestamp;
						p_documentation_data->rast_temp[status_automode-1] = setpoint;
						p_documentation_data->rast_max[status_automode-1] = max_temp;
						p_documentation_data->rast_min[status_automode-1] = min_temp;
						status_automode++;
						if(p_recipe->dekoktion[status_automode-1]%2)
						{
							dekoktion = true;
							dekoktion_setpoint =  p_recipe->rast_temp[last_rast];
						}
						else
						{
							dekoktion = false;
						}
						automode_time = p_recipe->rast_time[status_automode-1];
						check_rast = true;
						run_timer = false;
					}
					
				}
				break;
				default:
				break;
				}

Das ist erstmal ein Riesenblock und deckt auch nur einen kleinen Teil ab , da ich hier alles abfahre bis zum Läutern.

Das erste wichtige ist die erste Zeile

Code: Alles auswählen

switch(status_automode)
Die Bedinung für die Fälle ( Case ) ist der Inhalt der Variable status_automode.
Je nach Wert wird dann der Block ab dem passenden Wert bis zum nächsten

Code: Alles auswählen

break 
abgearbeitet.
Magic Numbers sind im Code immer schlecht und switch case, kann nur mit
Zahlen arbeiten. Daher die Definitionen weiter oben. Beim Compilieren wird der Text "status_mash_start" in den Wert 0 übersetzt. Wenn ich nun in der Mitte einen Schritt brauche änder ich einfach die nachfolgenden Definitionen ab. Beim Switch Case ist es egal wie die Reihenfolge ist, es wird immer der Fall abgearbeitet der passt. Es darf aber keinen Wert doppelt geben.

Der nächste wichtige Block ist die überprüfung ob die Rast angefahren wird.

Code: Alles auswählen

if(check_rast)
					{
						if(p_recipe->rast_temp[status_automode-1] > 25.0)
						{
							check_rast = false;
							last_rast = status_automode-1;
							min_temp = p_recipe->rast_temp[status_automode-1];
							max_temp = p_recipe->rast_temp[status_automode-1];
							setpoint =  p_recipe->rast_temp[status_automode-1];
							delta = setpoint - temp;
						}
						else
						{
							status_automode++;
							automode_time = p_recipe->rast_time[status_automode-1];
							check_rast = true;
						}
					}
Die Überprüfung findet nur einmal statt, da nach dem ausführen das Flag check_rast auf false gesetzt wird, wenn die Rast angefahren wird.
Wenn nicht bleibt sie true. Zudem wird der status auto_mode um einen um eins erhöht und im nächsten Durchlauf der Schleife wird die nächste Rast angefahren. Läuft also automatisch weiter. Wenn die Rast angefahren wird dann werden alle Paramter in die Variablen für den Regler geladen und es wird geprüft ob der Sollbereich erfolgt ist.

Code: Alles auswählen

if(delta <= 0.5 && delta >= -0.5 && gradient > -0.1)
					{
						run_timer = true;
						dekoktion = false;
					}
					if(run_timer)
					{
						if(min_temp > temp)
							min_temp = temp;
						if(max_temp < temp)
							max_temp = temp;
					}
					if(automode_time == 0)
					{
						p_documentation_data->rast_time[status_automode-1] = timestamp;
						p_documentation_data->rast_temp[status_automode-1] = setpoint;
						p_documentation_data->rast_max[status_automode-1] = max_temp;
						p_documentation_data->rast_min[status_automode-1] = min_temp;
						status_automode++;
						if(p_recipe->dekoktion[status_automode-1]%2)
						{
							dekoktion = true;
							dekoktion_setpoint =  p_recipe->rast_temp[last_rast];
						}
						else
						{
							dekoktion = false;
						}
						automode_time = p_recipe->rast_time[status_automode-1];
						check_rast = true;
						run_timer = false;
					}
Wenn das delta zum Sollwert kleiner als 0,5 K ist , startet der Timer. Wenn der Timer abgelaufen ist wird automatisch die nächste Rast angefahren.
Ich hab das direkt in der Main damit muss ich nichts übergeben.

Wenn du nun da ganze an ein Void übergeben willst musst du den Status für den Zugriff auf das Array bei dir übergeben. Wenn du folgendes machts

Code: Alles auswählen

void meine_raste(uint8_t status)
{
	Temperatur = Rast[status]; 
	status++
]
dann wird beim Aufruf der Wert der übergebenen Variable kopiert und mit der Kopie gearbeitet. In der Main bleibt der Wert aber erhalten.
Wenn du nun mit einem Zeiger den Wert übergibst, dann wird das direkt auch in der Main verändert.

Code: Alles auswählen

void meine_raste(uint8_t *status)
{
	Temperatur = Rast[*status]; 
	*status++
]
Du musst dann den Aufruf wie folgt machen

Code: Alles auswählen

meine_raste(&mein_status);
Du musst die Adresse der Variable übergeben da du nun mit einem Zeiger arbeitest. Damit kannst du den Status in dein Void ziehen und hier über die Arrays auf die Daten zugreifen.

Switch Case ist jedoch leicht erweiterbar und auch wieder leichter verständlich wenn du mal was in einem Jahr ändern musst.

Gruß JackFrost
Meine Hardware:
eManometer
IDS2 ohne CBPi
Magnetrührer
Ss-Brewtech 10 Gal Topf
IDS2 Induktionsplatte
christian_p
Posting Klettermax
Posting Klettermax
Beiträge: 231
Registriert: Dienstag 4. April 2006, 21:22

Re: Arduino Variable

#5

Beitrag von christian_p »

Hallo Jack,
danke für die sehr ausführliche Erklärung.
Leider übersteigt das Ganze ein bisschen meine Kenntnisse, ich werde es mir im Detail anschauen müssen um wenigstens einen Teil zu verstehen.
Hast du evtl. einen Tipp, wie es mit dem array funktioniert?
Also wie kann ich hinter meinem "T" eine Zahl setzen, so das es dann T1 oder T2... ergibt?
Danke nochmal für deine Mühe
Christian
Benutzeravatar
mwx
Posting Freak
Posting Freak
Beiträge: 789
Registriert: Mittwoch 11. Oktober 2017, 07:37
Wohnort: Berlin

Re: Arduino Variable

#6

Beitrag von mwx »

Hallo Christian,

das ist eigentlich recht einfach. Du musst das Array deklarieren und kannst dann direkt loslegen, z.B:

Code: Alles auswählen

int T[10];

for (i=0;i<10;i++) {

  tuwas(T[i]);

}
 	
Gruß, Michael

Gerührt und nicht geschüttelt: Magnetrührer Controller
JackFrost
Posting Freak
Posting Freak
Beiträge: 2984
Registriert: Dienstag 15. Mai 2018, 18:10

Re: Arduino Variable

#7

Beitrag von JackFrost »

christian_p hat geschrieben: Mittwoch 8. April 2020, 22:06 Hallo Jack,
danke für die sehr ausführliche Erklärung.
Leider übersteigt das Ganze ein bisschen meine Kenntnisse, ich werde es mir im Detail anschauen müssen um wenigstens einen Teil zu verstehen.
Hast du evtl. einen Tipp, wie es mit dem array funktioniert?
Also wie kann ich hinter meinem "T" eine Zahl setzen, so das es dann T1 oder T2... ergibt?
Danke nochmal für deine Mühe
Christian
Die Variablen kannst du nicht vom Namenändern.
Es geht nur das ganze als Array und hier dann über den Index zugreifen.
Alle deine Temperaturen müssen in einem Array stehen

Code: Alles auswählen

int T[8];
Über die Indizes 0 - 7 kannst du dann auf alle Temperaturen zugreifen.

Gruß JackFrost
Meine Hardware:
eManometer
IDS2 ohne CBPi
Magnetrührer
Ss-Brewtech 10 Gal Topf
IDS2 Induktionsplatte
christian_p
Posting Klettermax
Posting Klettermax
Beiträge: 231
Registriert: Dienstag 4. April 2006, 21:22

Re: Arduino Variable

#8

Beitrag von christian_p »

Hallo ihr beiden,
ich stand glaube ich auf dem Schlauch.
Dank euch und ein bisschen Youtube habe ich das array verstanden...hoffe ich.
Antworten