Mal wieder eine Frage zum Thema Speicher sparen

Ich definiere ein Array von Elementen:

#define MAX_ARRAY_SIZE 16
typedef struct {
  uint8_t quantity;
  uint16_t amount;
} Rows;
Rows rows[MAX_ARRAY_SIZE];

Im loop fange ich dann Tastencodes von einer PS2 Tastatur ab und reagiere entsprechend darauf

  if (keyboard.available()) {
    char c = keyboard.readUnicode();
    handle_keyboard(c & 0xFF);
}

handle_keyboard sieht so aus:

void handle_keyboard(char key) {
    static uint8_t row = 0;
    if (key == 0x2b) { //Eingabe
      rows[row].quantity = quantity;
      rows[row].amount = num.toInt();
      if (row > 1) {
        lcd_scroll_up(rows, row);
      }
      quantity = 1;
      num = "";
      row++;
    }

quantity ist ein uint8_t
num ist ein String mit Zahlen, der vorher von der Tastatur eingelesen wurde

Ist die Funktion:
void lcd_scroll_up(Rows rows[], uint8_t currentRow) {
...
}
so in Ordnung, oder wird da eine Kopie des Array "Rows" übergeben?
ich könnte das auch so schreiben:
void lcd_scroll_up(Rows* rows[], uint8_t currentRow) {
...
}
Macht das Sinn und gibt es da einen Unterschied im Speicherverbrauch?

Gruß
Fred

Rows* rows[]

Das ist ein Array aus Zeigern

Wenn du es deutlich machen willst dass da ein Zeiger übergeben wird mach es so:

void lcd_scroll_up(Rows* rows, uint8_t currentRow)

Das ist aber das Gleiche:

void lcd_scroll_up(Rows rows[], uint8_t currentRow)

Auch hier wird eigentlich nur ein Zeiger übergeben

Danke für die Infos
ich bin jetzt noch mal mein Projekt durchgegangen und habe mehrere globals durch statics in diversen Funktionen ersetzt und übergebe Objekte an Funktionen in der Art:
void foo (bar* myBar) {
}

Der freie Speicher hat sich vorher ständig verringert.
Nach mehr als 8 Tagen Laufzeit war der Speicher dermaßen fragmentiert, dass nur ein Reboot den ESP32 wieder zum Leben erweckt hat.

Jetzt tut sich da so gut wie gar nichts mehr. getFreeHeap() gibt (fast) konstant den gleichen Wert zurück.
Der Wert schwankt pro Stunde nur um ca. +- 100 Byte.

Man lernt eben nie aus.

Übrigens:
hab vor Monaten mal mit einem Java-Programmierer über deren Speicherverbrauch auf einem PC diskutiert. Das war seine Antwort: "Dann starte halt die Kiste neu".

Ich will aber meine ESP32 nicht ständig neu starten müssen (ist ja auch kein Java sondern C / C++ drauf) und endlich mal die 49Tage bis zum millis() Überlauf erreichen.

Am Schluss wurde ich auch noch als Neandertaler hingestellt, weil ich in C++ programmiere.
Die ganze Welt ist heute nur noch Java und C#.

freddy64:
ich bin jetzt noch mal mein Projekt durchgegangen und habe mehrere globals durch statics in diversen Funktionen ersetzt und
}

wie soll das Speicher sparen? Die static überlebt den Funktionsaufruf und das kann sie wohl nur im RAM.
Sie ist nun eben keine globale mehr sondern eine statische. RAM belegt sie dennoch dauerhaft.

Übrigens:
hab vor Monaten mal mit einem Java-Programmierer über deren Speicherverbrauch auf einem PC diskutiert. Das war seine Antwort: "Dann starte halt die Kiste neu".

Ich will aber meine ESP32 nicht ständig neu starten müssen (ist ja auch kein Java sondern C / C++ drauf) und endlich mal die 49Tage bis zum millis() Überlauf erreichen.

Am Schluss wurde ich auch noch als Neandertaler hingestellt, weil ich in C++ programmiere.

OT: welch ein "Spezialist", mehr braucht man dazu nicht schreiben.

Das war seine Antwort: "Dann starte halt die Kiste neu".

Zu WindowsXP Zeiten hatten wir den Verdacht, dass die vielen Security-Updates hauptsächlich den Zweck hatten, dass die Kiste neu gestartet wurde, um so Speicher-Lecks besser zu ertragen. OK, XP war kein Server-Betriebssystem, aber dass es normal sein sollte, die Kisten regelmäßig neu zu starten, war schon nervig.

wie soll das Speicher sparen? Die static überlebt den Funktionsaufruf und das kann sie wohl nur im RAM.
Sie ist nun eben keine globale mehr sondern eine statische. RAM belegt sie dennoch dauerhaft.

Aber sie wird nicht ständig neu in einem anderen Speicherbereich angelegt und fragmentiert dadurch den RAM.

Durch stundenlanges studieren von StackExchange und Co., habe ich jetzt über 4 kB freien RAM auf dem ESP32 "rausgekitzelt".

Strings wurden, dort wo es möglich ist, durch char's ersetzt und alle Variablen auf deren höchst möglichen Wert überprüft. 4 muss nicht vom Typ int sein. Da reicht auch uint_8.

Jetzt geht das Teil richtig ab.

@michael_x

Zu WindowsXP Zeiten hatten wir den Verdacht, dass die vielen Security-Updates hauptsächlich den Zweck hatten, dass die Kiste neu gestartet wurde, um so Speicher-Lecks besser zu ertragen. OK, XP war kein Server-Betriebssystem, aber dass es normal sein sollte, die Kisten regelmäßig neu zu starten, war schon nervig.

Ich glaube eher, dass durch die ganzen .NET Updates und Megabyte große Registries die Kisten immer langsamer wurden und die verzweifelten User in den nächsten MediaMarkt getrieben werden sollten. Das ist eine interne Absprache zwischen Intel und MS. Gibst du mir, so geb' ich dir.
Das hat man doch auch schon an den Hardware Bedingungen gesehen.
Prozessor > ixxxx > da gibt es keine Updates mehr für Windows7. Installieren sie gefälligst Windows10.
Das war die größte Schweinerei, die Intel zusammen mit MS ausgeklügelt hat.
Deshalb habe ich jetzt Linux und die beiden "Großen" können mich mal.

freddy64:
Aber sie wird nicht ständig neu in einem anderen Speicherbereich angelegt und fragmentiert dadurch den RAM.

du hast es oben im Zusammenhang mit "Speichersparen" geschrieben. Eine static spart gegenüber einer global keinen Speicher! Denn:
Global and Static variables are the first things loaded into SRAM. They push the start of the heap upward toward the stack and they will occupy this space for all eternity.

oder

Now to how SRAM is used: this is divided into three sections: at the low end are a collection of "static" variables, both those global variables you define in the sketch outside of functions and and ones defined elsewhere using the "static" keyword,

Adafruit hat da eine sehr gute Anfängerfreundliche Beschreibung, der ich traue: Optimizing SRAM | Memories of an Arduino | Adafruit Learning System

http://Strings wurden, dort wo es möglich ist, durch char's ersetzt

imho können ALLE Arduino-Strings durch c-strings ersetzt werden. Andererseits, und den Tipp möchte ich dir noch mitgeben, gäbe es auch die Methode .reserve() für Arduino-Strings. Das könnte helfen, Fragmentierung des Heap einzudämmen. reserve() - Arduino Reference

freddy64:

  if (keyboard.available()) {

char c = keyboard.readUnicode();
    handle_keyboard(c & 0xFF);
}

Wofür soll das c & 0xFF gut sein? C ist nur 8 Bit breit und damit nie größer als 0xFF.

Liefert readUnicode() wirklich nur 1 Char zurück?

Gruß Tommy

@Tommy

Liefert readUnicode() wirklich nur 1 Char zurück?

Ja, Statuscodes wie Ctrl, Alt, Shift ... kommen nicht mit.

Dadurch ergab sich aber ein anders Problem: 1, 8, 9 und einige andere Tasten sind dann doppelt vergeben.
Das ist aber bei dieser Nixdorf POS Tastatur kein Problem. Mit dem Plastik-Griffel die Taste herausziehen, um 90° verdrehen und schon sendet die Taste einen anderen Code.

Jetzt zu der Speicher-Grafik
So wie ich das sehe, belegen statics den gelben Bereich und fragmentieren dadurch nicht dem Heap.

Ein seltsames Verhalten des ESP32 habe ich noch festgestellt:
Wenn die WiFi Verbindung zu schlecht ist, geht das Result von getFreeHeap() nach einiger Zeit voll in die Knie.
Ich habe 2 baugleiche Kästen aufgebaut. Einer steht 3m von meinem WLan-Router entfernt, der andere 20m. Der mit dem schwächeren Signal frisst von Tag zu Tag immer mehr Speicher, bis er sich schließlich aufhängt. Sehr eigenartig ????
Also -> externe Antenne an den ESP, und schon läuft der Laden wieder besser.

Hi

Das kann aber doch keine gewünschte Lösung sein, oder?
Klingt ja fast wie der Spruch zu Win-XP, daß dort so häufig Updates rein kamen, daß die Kiste halt laufend neu gebootet werden musste, gar keine Chance hatte, älter als eine Woche zu werden.

Finde heraus, WAS Dir den Speicher zerhackt und dann, warum.

MfG

freddy64:
@TommyJa, Statuscodes wie Ctrl, Alt, Shift ... kommen nicht mit.

Was haben Ctrl, Alt, Shift mit Unicode zu tun?

Gruß Tommy

@Tommy
Die Lib liefert das so.
char c = keyboard.readUnicode();
liefert nur den Tastencode;
char c = keyboard.read();
liefert auch Status der Sondertasten.
Das Result ist dann immer 2 oder 3 Bytes lang.
Die PS2-Lib ist wahrscheinlich auch nicht so ausgereift. Bei schnell aufeinander folgendem Druck auf die gleiche Taste liefert keyboard.read() ständig andere Werte.
Da fehlt dem ESP einfach ein FiFo Puffer für solche Anwendungen.
Deshalb habe ich keyboard.readUnicode() verwendet.
Ist ja auch nur ein Demo-Projekt, das zeigt was passiert, wenn man nur 75€ in eine RFID Kasse investieren will.
Ich habe noch ein äquivalentes System mit Raspi und 13" Touch-Screen. Kostet "nur" 300€ ist aber um Welten besser.

freddy64:
Die Lib liefert das so.
char c = keyboard.readUnicode();
liefert nur den Tastencode;
char c = keyboard.read();
liefert auch Status der Sondertasten.
Das Result ist dann immer 2 oder 3 Bytes lang.

Weil das Resultat mehrere Bytes umfasst, liest Du das nur in 1 Char ein. Oder habe ich da was falsch verstanden?

Gruß Tommy