Kompilerfehler, Hardwaredefekt, oder DAU vorm Rechner?

Hallo,
das Verhalten meines Quellcodes bereitet mir gerade Kopfzerbrechen. Ich kann mir beim besten Willen nicht erklären was da los ist. Es werden Sensorwerte auf einem LCD ausgegeben und eine Ledbar zeigt einen einen entsprechenden Bargraphen, bzw eine Regenbogen Animation. Man kann über die Variable "screen" zwischen verschiedenen Bildschirmen für verschiedene Sensoren wechseln.

switch (screen) {
		case 	0:		{ Screens.main();  //case 0 funktioniert Einwandfrei
					LedBar.rainbow(10);
					} break;
		case 	1:		{ Screens.air();
                                        int16_t val = Sensors[3].get_value(); 
					if (val > 0) LedBar.bargraph(val, 150, Color::Green, Color::Red, true); //Hier wird der letzte Parameter "true" völlig ignoriert.
					else LedBar.bargraph((val * -1), 100, Color::Green, Color::Blue, false); // Hier ebenfalls
					} break; 
		case 	2:		{ Screens.oil();
					LedBar.bargraph(Sensors[1].get_value(), 700, 400, Color::Green, Color::Red, true);
					} break;
		case	3:		{ LedBar.bargraph(Sensors[4].get_value(), 1200, Color::Green, Color::Red, true);	 // Hier wird der zweite Farb Parameter völlig ignoriert, stattdessen nimmt er schwarz ( Null)			Screens.egt();
					} break; 
		case 	4:		{ Screens.oil2();
					LedBar.pointgraph(Sensors[5].get_value()-1000, 800, 470, Color::Green, Color::Red, true ); //Funktioniert       
                                        }break;
}

Unter case 1 wird der letzte boolsche Parameter der Funktion LedBar.bargraph() einfach ignoriert. Völlig egal was ich dort schreibe, er arbeitet immer mit false. Jetzt kommt das mysteriöse: Wenn ich zuerst den Bargraphen aufrufe, und danach erst Screens.air() nimmt er den Parameter korrekt entgegen. Ich kann mir das nicht erklären. Die beiden Funktionen haben eigentlich nichts miteinander zu tun, außer dass sie die gleichen Sensorwerte übergeben bekommen.
Unter Case 3 wird die zweite Farbe komplett ignoriert, egal was ich dort übergebe, er arbeitet immer mit schwarz (0x000000). Und auch hier funktioniert es wenn ich die Reihenfolge mit Screens.egt() tausche.
Was ist denn da los? Irgend eine Speicheradresse defekt an der dann zufällig die besagten Variablen landen? Kompilerfehler? Oder sitzt das Problem vor dem Rechner? Der Fehler ist 100% Wiederholbar.

void c_Screens::air() {
	float intakeP_f = IntakeP->get_value()/100.00;  //Wird später ohne float gehen, ist momentan nur angenehmer während ich noch ständig die Formatierung des Displays ändere.
	float maxP[2], holdP[2];
	maxP[1] = IntakeP->get_Max()[1]/100.00;
	maxP[0] = IntakeP->get_Max()[0]/100.00;
	holdP[0] = IntakeP->get_Peak()[0]/100.00;
	holdP[1] = IntakeP->get_Peak()[1]/100.00;

	char buffer[4][40];
	sprintf(buffer[0], "Ansaugluft (\337C | bar)        %3i | %5.2f", IntakeT->get_value(), intakeP_f);
	sprintf(buffer[1], "----------------------------------------");
	sprintf(buffer[2], "min:  %3i | %5.2f      max:  %3i | %5.2f", IntakeT->get_Max()[0], maxP[0], IntakeT->get_Max()[1], maxP[1]);
	sprintf(buffer[3], "hold: %3i | %5.2f      hold: %3i | %5.2f", IntakeT->get_Peak()[0], holdP[0], IntakeT->get_Peak()[1], holdP[1]);

	for (uint8_t i = 0; i < 4; ++i)
		for (uint8_t j = 0; j < strlen(buffer[i]); ++j)
			lcd->lines_set(i,j, buffer[i][j]);

}
void c_LedBar::bargraph(int16_t value, int16_t maxValue, c_Led color1, c_Led color2, bool invert) {
	uint16_t number = numPixels();
	if (value <= 0) {
		for (uint8_t i = 0; i < number; ++i)
			leds[i] = Color::Black;
		if (invert) leds[number-1] = color1;
		else leds[0] = color1;
	} else if (value >= maxValue){
		for (uint8_t i = 0; i < number; ++i)
			leds[i] = color2;
	} else {
		c_Led color;
		color.R(map(value, value, maxValue, color1.R(), color2.R()));
		color.G(map(value, value, maxValue, color1.G(), color2.G()));
		color.B(map(value, value, maxValue, color1.B(), color2.B()));

		value = map(value, 0, maxValue, 0, number);
		if (invert){
			for ( uint16_t i = 0; i < number; i++ )
				if ( value >= i) leds[number - i -1] = color;
				else leds[number - i -1] = Color::Black;
		}else{
			 for ( uint16_t i = 0; i < number; i++ )
				if ( value >= i) leds[i] = color;
				else leds[i] = Color::Black;
		}
	}
}

c_Led Color::Red(0xFF0000);
c_Led Color::Green(0x00FF00);

gsezz:
Oder sitzt das Problem vor dem Rechner?

Das soll vorkommen. Und wenn hier im Forum Anfänger nach Hardwaredefekten an ihrem Board fragen, sitzt das Problem sogar zu 99% vor dem Monitor eines PCs und das Board ist tatsächlich vollkommen in Ordnung.

gsezz:
Der Fehler ist 100% Wiederholbar.

Überprüfe den Gebrauch der geschweiften Klammern!

Du beendest beispielsweise Deine beabsichtigte switch-case-Anweisung innerhalb von "case 0:"!

Außerdem müssen in dem von Dir nicht gezeigten Code auch noch weitere Fehler mit den geschweiften Klammerungen vorliegen, sonst würde der Compiler das gar nicht fehlerfrei kompilieren. Keine Ahnung wo, denn Du zeigst ja nur ein Codefragment mit einem Klammerungsfehler, aber auf den ganzen Code bezogen kann das eigentlich nicht der einzige sein, den Du mit den geschweiften Klammern machst.

Wenn es irgendwann vorher funktioniert hat, denkst Du am besten mal scharf nach, wo Du zuletzt am Code mit geschweiften Klammern etwas geändert hast, bevor der Fehler auftrat.

Oh, sorry die Klammern sind wohl verschütt gegangen als ich den Code im Forum formatiert habe. Die Formatierung von Notepad++ passt hier nicht ganz, ich musste einige Tabulatoren löschen. In dem kleinen Editorfenster mit den vielen zeilenumbrüchen habe ich dann ziemlich den Durchblick verloren.
Im eigentlichen Quellcode stimmen die Klammer, sonst würde er ja wie gesagt auch gar nicht kompilieren. Ich habs nochmal überprüft und editiert.

Ich will hier nicht unnötig den gesamten Code posten der hinter jeder Funktion steht, das sind etliche hundert Zeilen. Ich habe nur die Ausschnitte gepostet die mir relevant erschienen. Wenn etwas bestimmtes wichtig wäre, bitte fragen, das ganze Programm hat über 3000 Zeilen.
Richtige funktioniert hat es noch nie. Aber der ignorierte bool Parameter aus case 1 funktioniert beim Funktionsaufruf unter case 3. Und die ignorierte Farbe aus case 3 funktioniert beim Aufruf in case 1. Und das auch wenn cih die jeweiligen Zeilen per Copy & Paste tausche. Also unter case 1 wird immer der letzte Parameter ignoriert und unter case 3 der vierte. Ich habe sogar versucht die Parameter der Funktion zu tauschen, also bspw maxValue an die vierte Stelle, dann wird maxValue ignoriert und mit Null gearbeitet.

Btw da waren auch noch ein paar (bool) casts drin, "true" und "fasle" als int betrachtet wurden. Ich habe mittlerweile auf 1.5.6r2 zurück gewechselt, die kommt ohne cast zurecht. Deshlab hatten im geposteten code noch manche Parameter einen cast und andere nicht. Ich habe nun alle casts raus genommen.

Ach, und es geht um einen DUE, das hatte ich ganz vergessen zu erwähnen.

Ohne gesamten Sketch, Schaltung und genaue Beschreibung (link) der verwendeten Hardware (außer Arduino DUE, das wissen wir in der Zwischenzeit) können wir dir nichts genaues sagen.
Grüße Uwe

Ich habe nun noch etwas experimentiert
Es ist eine einfache Parameterübergabe die nicht funktioniert, und zwar nur wenn die Funktion genau an dieser Stelle des Codes aufgerufen wird:

LedBar.bargraph(Sensors[4].get_value(), 1200, Color::Green, Color::Red, true);

Parameter Nr. 4 kommt nicht an, stattdessen enthält der Parameter den Wert Null. Egal welcher Parameter der vierte ist, in dieser Zeile bekommt er immer den Wert Null. In der anderen Zeile ist es der fünfte der immer Null enthält.
Nun habe ich testweise die Funktion auskommentiert und stattdessen fünf Variablen gefüllt und ausgegeben:

case	3:  {uint32_t Test1, Test2, Test3, Test4, Test5;
  Test1 = 122;
  Test2 = 133;
  Test3 = 155;
  Test4 = 166;
  Test5 = 177;
  Serial.println(Test1);
  Serial.println(Test2);
  Serial.println(Test3);
  Serial.println(Test4);
  Serial.println(Test5);
  delay(10000);
/* .....

Das ist die Ausgabe:

122
133
155
0
177

Das ist kein Verhalten dass ich mir durch einen Fehler im Quellcode erklären kann, bin aber eben nur Laie. Ich kann es leider nicht auf anderer Hardware testen weil ich nur einen DUE habe, und das Programm nicht auf die kleineren Brüder passt.

Ich kann gerne das ganze Projekt irgendwo hoch laden. Nur gehe ich nicht davon aus dass Jemand Lust hat sich durch den ganzen, zum größten Teil noch unkommentierten Code zu arbeiten. Darum würde ich auch bestimmt niemanden bitten. Das sind aktuell etwa 15 Header plus einige Libs.
Ungenaue Aussagen können manchmal doch auch zielführend sein. :wink:

Hier hab ich ein komplettes Programm "für dich"

void setup()
{
   Serial.begin(9600);
}

void loop ()
{uint32_t Test1, Test2, Test3, Test4, Test5;
  Test1 = 122;
  Test2 = 133;
  Test3 = 155;
  Test4 = 166;
  Test5 = 177;
  Serial.println(Test1);
  Serial.println(Test2);
  Serial.println(Test3);
  Serial.println(Test4);
  Serial.println(Test5);
  delay(10000);
}

Was macht das bei dir?
Ich schätze mal, das läuft wie erwartet.

Das bedeutet dann, das Problem steckt nicht in dem, was du gepostet hast.

Dass ich mir keine 3000 CodeZeilen und 15 Libraries ansehen möchte, hast du leider auch Recht :wink:

Beim Uno wäre das wohl ein Fall von RAM - Überlauf,
beim DUE sind > 64kB RAM zwar nicht unendlich, aber schonmal was...

Ich schlage vor, du schmeisst die Hälfte deiner 3000 Zeilen raus (testweise) und schaust, ob das Problem mit verschwindet ...

Bei dem Programm tritt kein Fehler auf. Hätte ich auch nicht anders erwartet, schließlich tritt er im Hauptprogramm auch nur exakt an dieser Stelle auf. Füge ich den Code wo anders ein werden die fünf Werte korrekt ausgegeben.
Es werden halt auch nur 5 Variablen benutzt, während in meinem Hauptprogramm sicher schon ein paar hundert im Speicher liegen, wenn ich meine fünf Tests zuweise.

Ich schlage vor, du schmeisst die Hälfte deiner 3000 Zeilen raus.

Leichter gesagt als getan die Klassen haben sich alle so gern. :wink: Aber ich habe mal versucht kleine Teile auszukommentieren, so dass es sich halt noch kompilieren lässt. Egal was ich auskommentiere, der Fehler ist weg.
Ich habe z.B. das auslesen und Interpolieren von Sensorwerten gestrichen und durch feste Werte ersetzt, Fehler weg. Oder ich lasse das drin und streiche stattdessen sämtlichen Code der mit Buttons zu tun hat, Fehler auch weg. Quasi jede gröbere Änderung, egal wo, scheint den Fehler an dieser Stelle zu beheben.

EDIT:
Das unten gilt nur für einen AVR. Hatte überlesen, dass es sich hier um einen Due handelt. Da geht das so nicht.

Deutet in der Tat auf RAM hin. Überpüfe mal mit einer lauffähigen Version was noch frei ist:

int getFreeRAM() 
{
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Dann um das zu beheben.
1.) Überall wo du print()/println() mit Strings verwendest ein F() um die String Konstante machen:

Serial.println(F("String im Flash"));

2.) Die _P Versionen von String Funktionen verwenden! Das gilt was ich da sehe vor allem für printf():

sprintf_P(buffer[3], PSTR("hold: %3i | %5.2f      hold: %3i | %5.2f"), ...);

Mit _P und dem PSTR() (für Progmem String) bleibt der Format String dann im Flash und wird nicht ins RAM kopiert

Das hier ist auch nicht so toll:

char buffer[4][40];

Ist zwar nur auf dem Stack, aber der Speicher muss trotzdem da sein. Lege lieber ein Array mit 40 Zeichen an, dann machst du sprintf(), gibt den Puffer gleich aus, und machst dann erst wieder sprintf() mit anderen Parametern.
Also einen Puffer für alle 4 Zeilen verwenden.

Nachtrag:
%f funktioniert auf dem Arduino standardmäßig sowieso nicht mit printf()! Das hat man weggelassen, da es die Funktion noch weiter aufblähen wurde. Das läuft zwar, gibt aber dann Fragezeichen an der Stelle aus.

ob getFreeRAM() auf dem Due mit mehr als 64 kB RAM und 32 bit Zeigern funktioniert ?

Ah shit. Ein Due. Das hatte ich ganz überlesen :frowning:

Sorry. Dann geht das oben mit PROGMEM sowieso nicht. Und auch printf() sollte mit %f gehen.

Tja, mit 32 bit Details beim Due bin ich auch überfragt.
Aber wenn da ein int 32 bit groß ist, könnte gsezz sich das getFreeRAM ja mal ansehen.

Entweder gibt es Probleme, wenn eine 64 kB Bank überläuft oder eben spätestens bei 96 kB.

Allein dass das Entfernen von nur einem kleinen Teil des Codes das Problem verschwinden lässt, deutet sehr stark auf einen Speicher-Überlauf hin ...

getFreeRAM() bezieht sich allerdings auch explizit auf den Speicher der AVRs:
http://www.nongnu.org/avr-libc/user-manual/malloc.html

Keine Ahnung wie das bei ARM Prozessoren ist

Wenn Englisch-Kenntnisse vorhanden sind, lieber mal im Due Forum hier nachfragen

Serenifly:
Keine Ahnung wie das bei ARM Prozessoren ist

Auf dem Arduino DUE kann man das verwenden:

extern "C" char* sbrk(int incr);
int getFreeRAM()
{
  char top;
  return &top - reinterpret_cast<char*>(sbrk(0));
}

Ok, danke, das werde ich heute Abend mal versuchen. Ich habe einige Funktionen und Libs dafür gegoogelt, aber die schienen auf dem DUE alle nicht zu funktionieren.

Ausgabe im leeren Sketch: 95915
Ausgabe in meinem Programm: 90039
Also erst 6kB verbraten, hätte doch deutlich mehr geschätzt.

Frage am Rande: int8_t und int16_t belegen auch auf dem Due nur 1, bzw 2 Byte, oder?

Aber bis auf diese eine Stelle (zwei case Fälle treten ja quasi an der gleichen Stelle des Programmablaufs auf.) gibt es auch keinerlei Probleme. Keine merkwürdigen Sensorwerte oder anderes abnormales Verhalten.

gsezz:
Aber bis auf diese eine Stelle (zwei case Fälle treten ja quasi an der gleichen Stelle des Programmablaufs auf.) gibt es auch keinerlei Probleme. Keine merkwürdigen Sensorwerte oder anderes abnormales Verhalten.

Die Anzahl der offensichtlich erkennbaren Fehler sagt nichts darüber aus, ob nicht noch viel mehr Fehler im Programm enthalten sind, die sich nur derzeitig nicht auswirken.

Wenn ausreichend RAM vorhanden ist, werden von Anfängern auch Indexfehler bei Arrayzugriffen immer gerne genommen:

  int test[10];
  test[10]=4712;

Und schwupps wurde mit dem Code um genau 2 Bytes HINTER dem deklarierten Integer-Array ins RAM geschrieben.

Kenne ich, passiert mir aber höchstens aus Unachtsamkeit.

Aber der Parameter einer Funktion wird doch in dem Moment in den Speicher geschrieben in dem die Funktion aufgerufen wird. Und wenn ich innerhalb der Funktion als aller erstes diesen Parameter ausgebe kann er doch nicht zwischenzeitlich von etwas anderem überschrieben worden sein, oder? Abgesehen von interrupts, und ich nutze keine. Eventuell eine der libs, aber die sind alle recht populär (Neopixel, Liquidcrystal440, DS3231, TinyGPs, und ähnliche, nichts exotisches.)

Frage am Rande: int8_t und int16_t belegen auch auf dem Due nur 1, bzw 2 Byte, oder?

Ja. sizeof, oder getFreeRAM()...reden wir nicht drüber, langer Tag. :wink:

sind die 6kB denn plausibel, oder liefert sbrk() evtl. schon eine -1 zurück.

Fehlerabfragen sind nicht üblich bei Arduino Software, ich weiss.

Also wenn ich ein zusätzliches Array von 100 32bit variablen einbaue sinkt der Wert um genau die 400 byte. Ich muss mal zusammen rechnen was ich deklariert habe, sind schon einige Arrays, aber ich nutze immer den kleinsten möglichen Datentyp. Andererseits bestehen viele nur innerhalb von Funktionen, ich kann das gerade nur schätzen, aber das dürfte schon realistisch sein. Zumindest gibt es nichts das bewusst annähernd Richtung 96kb gehen könnte. Wobei ich denke dass die Libs einen großen Teil ausmachen, und deren Code verstehe ich oft nicht im Details, gerade bei dynamischen Zuweisungen.
Ich werds heute Abend mal zusammen rechnen.

Wenn du ein Array mit 32bit (=4 Bytes) Variablen anlegst, ist der Speicherverbrauch von 400 Byte ganz normal.

Zu deinem vorhierigen Beitrag, dass die Libs alle so populär sind, bezweifel ich. Für die Neopixel wird eher auf die FastLed Lib zurückgegriffen, wenn es den Leuten bewusst ist. LiquidCrystal440 sagt mir erstmal garnichts. DS3231 gibt es einige und meist reicht es, wenn man sich den Teil sogar selber schreibt, denn vieles wird meist dann doch nicht benötigt.