Button für Lieblingskarte

@christoph_e
danke das war eine Gedankenübertragung

Jetzt hab ichs, ich glaub du warst schneller, gerade habe ichs zeitgleich probiert

         String lieblingPlayed = "057166229151";
      char *lieblingsKarte = strdup(lieblingPlayed.c_str());

kann mir jemand kurz erklären was dieses strup macht?

danke danke danke

:+1: :+1: :+1: :+1:

also ich habs mit dem String probiert, jetzt probier ich es noch mit deinem Tip
mit char *:
Mit char * kommt ein Kompilierungsfehler…aber folgende Beschreibung mit String funktioniert

  String lieblingPlayed = "057166229151";
  char *lieblingsKarte = strdup(lieblingPlayed.c_str());

  xQueueSend(rfidCardQueue, &lieblingsKarte, 0);

strdup reserviert Speicher mit malloc: strdup
Das könntest du auch direkt machen.

Was macht xQueueSend mit dem Pointer oder dessen Inhalt? Bin nicht mit der FreeRTOS Spec vertraut.

@christoph_e

vielleicht ist ja das

.c_str()

auch noch wichtig??

Also es wird der Link der zugehörigen KartenID (egal ob mp3, webstation…) abgespielt.
Das mit dem free habe ich nicht reingeschrieben, da es ohne funktioniert hat…wäre das wichtig für mehr Speicher?

Lösung:

  String lieblingPlayed = "057166229151";
  char *lieblingsKarte = strdup(lieblingPlayed.c_str());

  xQueueSend(rfidCardQueue, &lieblingsKarte, 0);
1 „Gefällt mir“

Ja, den Speicher solltest du wieder freigeben.

@christoph_e
meine Vorlage war „void recoverLastRfidPlayed“

da wurde der Speicher nicht freigegeben…wäre das sinnvoll wenn das dort auch im Orginalcode gemacht würde?

danke

Ich habe leider noch keine Hardware und Überblick über den Code. Das muss jemand anderes beantworten.

Ansonsten hab ich hier ein Bespiel auf eine Frage gefunden, in dem auch explizit der Speicher reserviert wird. https://esp32.com/viewtopic.php?f=18&t=3086#p14468

Ich könnte mir aber vorstellen, dass die KartenId evtl. im Speicher bleiben soll, bis eine neue Karte aufgelegt wird…um so an evtl. benötigte Daten des gespielten Links zu kommen…bei Pause und Play, bei streamtitle oder Abfrage der streamstation??? Nur so ein Gedanke…wie gesagt, ich habe sehr sehr wenig Ahnung von dem ganzen Variablen, RAM, PSRAM und c++

Das wichtigste ist, dass es läuft… :blush:

Aaalso, ich schreibe mal was dazu :slight_smile:
Wenn man lokal (und nicht global) mit z.B.

Speicher reserviert, dann wird dieser Speicher auf dem Stack allokiert. D.h. du erhältst einen Pointer und dieser markiert die Anfangsadresse deines Char-Arrays. Wenn deine Funktion jetzt aber durchgelaufen ist, dann wird der Speicher automatisch wieder freigegeben. D.h. er kann und wird anderweitig wiederverwendet werden.

Das führt dann zu Folgendem: Du übergibst die Anfangadresse deines Char-Arrays an die Queue und aber kurz danach wird der Speicher freigegeben. Die Funktion rfidPreferenceLookupHandler() empfängt nun den Inhalt der Queue - einen Pointer. Und während sie damit arbeitet kann (muss nicht) es passieren, dass der Speicher ganz oder teilweise überschrieben wird. Der Pointer führt dann also zu einem Speicherbereich, der undefinierten Inhalt besitzt. Und dann knallt es eben.

Was kann man da machen? Ganz einfach: Man allokiert Speicher auf dem Heap. Damit reserviert man eine gewisse Größe an Speicher und erhält einen Pointer zurück, der die Anfangsstelle markiert. Vorteil: Der wird nicht überschrieben. Nachteil: Man ist selbst dafür verwantwortlich, ihn wieder freizugeben :slight_smile: Tut man das nicht, dann ist der Speicher irgendwann entweder wegen Heap-Fragmentierung voll sein oder eben weil er wirklich voll ist :slight_smile: Man spricht von einem Memory-Leak. Allerdings muss man hier die Taste vermutlich einigermaßen oft drücken, bis sich das mal auswirkt.

Heap-Speicher allokiert man üblicherweise mit malloc() oder calloc(). strdup() ist eine Komfortfunktion, die ich auch gerne verwende: Sie allokiert Speicher in der passenden String-Größe (n+1), gibt einen Pointer zurück und kümmert sich auch um die \0-Terminierung.

Begriffe:
\0-Terminierung: Char-Arrays müssen \0 terminiert sein, damit bei einem Char-Array klar ist, wo es endet. D.h. wenn man einen String mit zehn Zeichen hat, benötigt man 11 Zeichen Speicher.

Heap-Fragmentierung: Speicher muss immer zusammenhängend allokiert werden. Nehmen wir an, wir hätten 20 Zeichen Heap noch frei und müssten 15 Bytes allokieren. Wenn jetzt in der Mitte der 20 Bytes z.B. 2 Bytes allokiert wären, dann würde die Allokation fehlschlagen, weil das zusammenhängend nicht klappt. Und wenn man sich den Speicherbereich viel größer vorstellt mit lauter so kleinen bereits allokierten Blöcken, dann nennt man das Heap-Fragmentierung. D.h. obwohl eigentlich genug Speicher frei ist, klappt es dennoch nicht. Deswegen muss man eigentlich, wenn man das ganz ordentlich macht, auch prüfen, ob die Allokation wirklich geklappt hat (Pointer != NULL) :slight_smile: Verlässt man sich einfach drauf, dass es geklappt hat, dann kann es zu komischen Effekten kommen :smiley:

Hoffe das bringt Licht ins Dunkel :slight_smile:

3 „Gefällt mir“

@biologist
danke für die ausführliche Erklärung.

Wenn ich free(lieblingsKarte); nach dem xQueueSend und sogar nach delay(2000); eingebe…kommt wieder Absturz und Neustart…also lass ich den reservierten Heap-Speicher belegt.

Am PC ist also alles viel einfacher, weil man „schlampig“ programmieren kann, und egal wieviel Speicher verprassen kann…aber bei diesen kleinen Dingern ist die Speicherverwaltung wohl sehr wichtig…sind „spanische Dörfer“ für mich.
…und jetzt drücke ich die Taste „lieblingsKarte“
:innocent:

Ja das bringt auch nix, weil das hat den gleichen Effekt wie auch dem Stack :slight_smile: Freigeben kann man das erst, nachdem man es verarbeitet hat. Also an anderer Stelle.

ja ist dann wie char *lieblingsKarte = „xyz“

was bedeutet dann noch das .c_str()

…wenn ich hier schon im c++ Kurs für blutige Anfänger bin…finde echt gut wie biologist mir das erklärt.
Für die anderen wahrscheinlich ziemlich langweilig, aber ich bin dankbar…

zählt das die Zeichen?

Das macht aus einem String-Objekt (welches in C++ gibt, nicht jedoch in C) ein Char-Array.
Empfehlung für C: Rheinwerk Computing :: C von A bis Z
Dort in Kapitel 14 geht es z.B. um dynamische Speicherverwaltung.

@biologist
danke, dann werde ich mal wirklich mit dem Lesen beginnen

Um das Probem mit der speicher zuweisung funktionsübergreifend zu lösen. erstell die die pointer Variable einfach global :wink:

Das mit dem free() hatte ich von hier übernommen https://github.com/biologist79/ESPuino/blob/a8b259b526e2fbbb47f78d4279c88606ff949e29/Hardware-Plaforms/ESP32-A1S-Audiokit/src/main.cpp Zeilen 686-690:

    else if (strcmp_P(topic, topicTrackCmnd) == 0) {
        char *_rfidId = strdup(receivedString);
        xQueueSend(rfidCardQueue, &_rfidId, 0);
        free(_rfidId);
    }

Unabhängig davon: Liegen String Literale (const char * literal = „12345“)nicht eigentlich in .rodata und nicht auf dem Stack? Das war ursprünglich meine Hoffnung und man somit weiter auf eine gültige Speicheradresse zeigt. Die Realität war aber ja: es hat nicht geklappt :smile:

Ja, da haste mich erwischt. Das ist ein Bug :rofl:. Glaube aber inzwischen hatte ich das korrigiert, weil es tatsächlich zu Problemen geführt hatte. Den a1s-Zweig supporte ich eigentlich nicht mehr. Sollte ich mal löschen.
Aber wie Daniel schon sagte: Deklariere es global (irgendwo ganz oben in main.cpp) und du hast das Problem nicht.