Aufteilen bzw. zerteilen von Chars

  • Hallo Kai,


    super, man legt quasi nur die 64bit Variable fest und kann so entsprechend umrechnen.


    Und ja der Simulator ist super, so kann man auch am Tablet oder anderem Device Versuche anstellen, ohne den Mikrocontroller angeschlossen haben zu müssen. Hatte nach deinem Post mit dem C Simulator nach etwas für Arduino gesucht und bin dan auf den Erwähnten gestoßen :)


    Naja, scheinbar simuliert er nicht zu 100% denn der Serial.println Befehl geht auf Fehler:


    call of overloaded 'println(uint64_t&)' is ambiguous


    Ein kurzes Anwerfen von Google brachte aber eine Lösung. Hier gibt es fertige Klassen die 64bit integer serial ausgeben können.

    Z.B. How to Print uint64_t in Arduino
    Da ich gern auf verfügbare Bibliotheken die ich über Arduino finden und einbinden kann zurückgreife habe ich alternativ die printHelpersgefunden
    Nachdem ich diese eingebunden habe und den Print Befehl in Serial.println(print64(erg)); abgeändert habe, wird es ohne Probleme auch von der Arduino IDE kompiliert und auf den Controller übertragen. Das angezeigte Ergebnis stimmt.

    Herzlichen Dank für deine Arbeit und Zeit !!!!!!

  • Ich habe eine Lösung gefunden. Habe jetzt selber wieder etwas dazugelernt :).

    Zumindest funktioniert es laut dem Simulatorprogramm mit dem ESP32.



    Das dürfte ordentlich Speicherplatz kosten. Aber im Prinzip funktioniert es.


    Diese Simulationsseite kannte ich noch nicht... die ist prima.

  • Zitat

    Ich nehme mal an ich muss das noch etwas ändern
    for(int i=strlen(input)-1, int y=0; i > -1; --i, ++y){


    Nein. Du musst nichts ändern. Das zweite int vor dem y wird einen Fehler produzieren. Du kannst beim Arduino Framework in der For-Schleife mehrere Variablen definieren aber nur von einem Datentyp. Darum reicht ein int am Anfang.


    Ich kann es nicht mit Sicherheit sagen, aber auch bei Micropython bleibt ein 8-Bit Mikrocontroller ein 8 Bit Mikrokontroller. Darum bezweifle ich, dass dort 64Bit für eine Zahl zur Verfügung stehen.

  • Ich habe mal versucht den Hex Wert auf eine sehr einfache Art umzurechnen.
    Wie Kai schon erwähnt hat ist das nicht so einfach


    Wenn ich das hier probiere wird der max Wert des Datentypes uint32 4294967295 angezeigt.
    Ich bräuchte somit einen typ uint64. Anscheinend kann das der Arduino nicht.

    Wäre eine andere Programmiersprache eine Alternative z.B. Micropython?

    unsigned long s=((3 * pow(16,9))+(10 * pow(16,8)) + (3 * pow(16,7))+(15 * pow(16,6)) + (2 * pow(16,5))+(9 * pow(16,4)) + (10 * pow(16,3)) +(1 * pow(16,2)) + (07 * pow(16,1))+ 1);

  • Danke auch dir Pius, habe nach dem Schreiben meiner Antwort gesehen dass du geantwortet hast.

    Die Idee, Dinge direkt auszuschließen, finde ich gar nicht so verkehrt. Wenn man sich einen bestimmten Stil angewöhnt stolpert man vielleicht nicht so schnell und verringert das Risiko eine lange Fehlersuche durchführen zu müssen.


    Und ja, wenn man weiß wie sich eine Sprache verhält und wie man mit ihr umzugehen hat ...


    Tatsächlich habe ich das so ähnlich probiert
    Temp[11] = 0 - ich wollte den kompletten char mit Nullen füllen, was irgendwie nicht geklappt hat.

    Ich werde, um beide Varianten der Nullterminierung zu verstehen, Beide mal anwenden

  • Jetzt habe ich sogar den unteren Teil deines Beitrages #19 übersehen. Hat mir mein Pad garnicht angezeigt.


    Danke für das Vereinfachen des Codes. ich hatte das schon über zwei verschachtelte For Schleifen probiert und bin erst einmal gescheitert. Ich war froh es hinbekommen zu haben. Dein Code hilft mir das Thema For Schleife besser zu verstehen. Hatte auch nicht gewusst mehrere Bedingungen einfügen zu können. In der Befehlsreferenz von Arduino steht es nur so


    for (initialization; condition; increment) {

    // statement(s);

    }


    Ich nehme mal an ich muss das noch etwas ändern
    for(int i=strlen(input)-1, int y=0; i > -1; --i, ++y){


    Zum Thema Hex to Dec:


    Du hast natürlich recht, für das reine Vergleichen der Chip ID mit der zugelassenen ID benötige ich keine dieser Umrechnungen. Ich könnte hier direkt bestimmen. Das Thema lässt mich halt nicht so los. Ein Nice to have ist zudem die Anzeige der Daten auf dem Display und vielleicht später das Speicher n in einer kleinen Datenbank. Auch wenn das total überdimensioniert ist.

    Diese Art von Projekten bringen mich dazu die Programmierung und den entsprechenden Hardwareteil besser zu verstehen.

  • Nun, vielleicht ist das der Grund weshalb ich strncpy() wenig benutzte. Wenn ich weiss, dass ich n Zeichen kopieren muss, dann benutze ich memcpy(), da strncpy() höchstens die Anzahl Zeichen kopiert oder solange die Grenze nicht erreicht, kopiert es bis zum 0. strncpy() , sofern richtig angewendet, vehindert, dass der Zielspeicher übers Ende hinaus beschrieben wird.


    char Temp[12];

    Temp[11] = 0; // vorab den 0 Terminator schreiben

    strncpy(Temp, "Das ist der Text der laenger als 12 Zeichen ist", sizeof(Temp)-1);


    (Ich habs nicht ausprobiert ... mein PC ist noch nicht soweit installiert).

    Nun, Mike es ist eben so dass C nichts automatisch für dich macht. Es macht das gemäss der Definition, was genau die Stärke der Sprache ausmacht. Du kopierst ein Byte, dann willst du auch nur einByte kopiert wissen und nicht dass etwas zusätzlich angehängt wurde. Leider ist dies eine der häufigsten Fehler der gemacht wird. Es ist also nur normal, dass man über solche Dinge strauchelt. Mein Rat, so klein wie möglich beginnen und jede Zeile lernen zu verstehen.
    Leider hat man in der Arduino Umgebung keine Möglichkeit zu debuggen, das Programm Zeile für Zeile ausführen zu lassen, und dazwischen jeden Inhalt zu betrachten.

    Es wird wieder vorkommen, dass du die abschliessende 0 übersiehst, aber es wird dann immer seltener werden.

    Gruss

    Pius

  • Hab ich schon gelesen, jedoch wohl nicht komplett verstanden bzw. ich hatte angenommen die abschließende NULL Terminierung funktioniert automatisch. Und scheinbar habe ich das hier überlesen:


    Zitat

    Es ist natürlich auch möglich einen String mit "memset" immer wieder mit Nullen zu füllen. Das dürfte aber länger dauern, als wenn nur ein stadt[7]= "\0" verwendet wird.


    Ich habe noch viel zu lernen ;(

  • Irgendwie muss das Posting #8 und #9 komplett übersehen worden sein.


    Dein Reversieren kanns Du etwas vereinfachen:

    Hier brauchst Du kein Array Offset.


    Die Zahl in Dezimal umzurechnen ist gar nicht so trivial, weil die Zahl zu groß ist um sie in eine 32 Bit Integervariable unterzubringen.

    Aber vielleicht ist das ja auch gar nicht nötig wenn du nur mit den Hexwerten arbeitest.


    Warum brauchst Du die Dezimalwerte?


    Es geht doch wahrscheinlich nur darum einen eingelesenen Wert eines Chips mit einem gespeicherten Wert zu vergleichen um zu erkennen ob der Chip zulässig ist.

  • Deshalb klappt es manchmal und manchmal nicht da noch Reste im Speicher sind, richtig?
    Probier ich morgen mal aus. Vielen Dank Pius!


    Kannst du mir einen Tip geben wie ich wie in meinem letzten Post den Wert in Dezimal umrechnen kann?

  • nicht mit 0 abgeschlossen.

    Nun ich benutzte memset() voraus, weil ich wusste wie gerne dieser Fehler passiert.

    Das Zeichen nach der letzten Ziffer muss eine binäre 0 sein.

    setzte vor die Zeile 26:


    memset(nId, 0, sizeof(nId));



    Gruss

    Pius

  • Je mehr ich mich damit beschäftige desto mehr verstehe ich. Jedoch passt die Ausgabe beim Ländercode noch nicht.

    Manchmal funktioniert sie und. zeigt den 4 stelligen Wert und dann wird die zuvor gelesene nId angehangen.


    230323430373346303233343833303031303030303030303030307B843

    Chip ID: 304073F023

    Ländercode: 48304073F023 <-- korrekt wäre 4830



    Um die richtige ID auszugeben muss ich sie in Dezimal umwandeln. Dies klappt leider nicht, die Funktionen die ich gefunden habe scheinen immer nur einzelne Bytes umzurechnen, um auf das richtige Ergebnis zu kommen muss der komplette Wert genutzt werden.

    Habt ihr eine Idee?


    Hier ein Beispiel wie ich die nId gedreht habe, geht wahrscheinlich eleganter ;)
    Code im Online Simulator

    Aus dem Hex Wert 171A9253A3 wird dann 3A3529A171. Der richtige Dezimalwert ist 250000023921.

  • Jedes Programm begint mit Zeile 1:)

    char input[]; müsste dir beim Übersetzen eigentlich einen Fehler erzeugen, da die Grösse des Arrays nicht definiert wurde.

    Ich kann nur vermuten, dass lediglich 1 char gespeichert werden kann (sofern kein Fehler aufgetaucht ist).


    Also, defniere eine Grösse:

    char input[32]; // reserviert 32 Char für das Array


    im nächsten Schritt musst du einschätzen, wie gross das grösste Array benötigt wird, damit du die Schlaufe abbrechen kannst, wenn dein Array gefüllt ist.
    Wird in einem Array über den reservierten Bereich hinaus geschrieben, dann werden andere Daten überschrieben. Dies führt zu ganz seltsamen Verhalten des Programms.


    Gruss

    Pius

  • Jetzt stehe ich vor meiner nächsten Herausforderung 🤭


    Das Zerteilen und weiterverarbeiten eines Char ist mir jetzt soweit klar.

    Nun möchte ich den zuvor manuell gesetzten Wert seriell einlesen.


    Einlesen über seriell2 und ausgeben über seriell1 funktioniert gut über Seriell2.read() und Seriell.print().

    Wenn ich nun aber die Daten in die Variable speichern möchte funktioniert das nicht mehr.


    Muss ich hier Byteweise einlesen, meine Versuche scheitern hier?


    Code
    1. char input[];
    2. int i=0;
    3. for(i=0;(Serial2.available()>0);i++){
    4. input[i]=Serial2.read();
    5. }

    Als Hintergrund: Ich versuche für unsere Katzen eine Zugangskontrolle mit einem RFID Reader zu bauen und möchte kontrollieren, welche Katze Zugang hat und den Versuch auch zu protokollieren.

    Die auf den ersten Blick einfach erscheinende Aufgabe des einlesen in eine Variable …


    Das Modul ist ein WL-134

    Beschreibung auf Aliexpress

  • Kai, das weiss ich, du bist viel gezielter in der Vorgehensweise als ich.:(


    Was macht der Compiler aus dieser Zeile?


    char str[] = {'H','e','l','l','o','\0'};


    Er legt einen Speicherbereich vom Typ char mit der Länge von 6 Zeichen an. Anschliessend füllt er diesen Speicher mit der String Konstanten auf. Wird Code für einen Atmel Prozessor erstellt, dann muss er berücksichtigen, dass «Hello» im PGM Memory steht wie der Programm Code selbst. Str hingegen liegt im Ram. Ich weiss nicht, ob es davon abhängig ist, wo im Code die Zeile steht. Wird str ausserhalb einer Funktion deklariert, dann wird (vermutlich) das Abfüllen von str vor dem ersten Aufruf der ersten Funktion (sei es main()/setup() oder was immer) ausgeführt.

  • @Pius: Ich wollte es fast genau so schreiben. '\0' ist ein Stringliteral für 0. Letztendlich kommt beides auf dasselbe heraus.


    char str[] = {'H','e','l','l','o','\0'};


    Bei den Anführungszeichen habe ich mich vertan.

  • Mike, mit der «Stringverarbeitung» in C tut man sich anfänglich schwer, weil C (zu Recht) keinen Basistyp dazu bereitstellt. Vielleicht ist dies einer der Umstände, weshalb immer wieder behauptet wird, C sei schwierig.


    Es ist pures C, und Kai weisst zu Recht auf die Details (z.B: strncpy(), strcpy() etc) hin. Der Programmierer muss die Details kennen, tut er es nicht, wird er sie kennenlernen, spätestens dann, wenn es schief gegangen ist und dann tut es weh.

    Zitat

    Es ist natürlich auch möglich einen String mit "memset" immer wieder mit Nullen zu füllen. Das dürfte aber länger dauern, als wenn nur ein stadt[7]= "\0" verwendet wird.

    Kai, da wolltest du vermutlich stadt[7] = 0; oder stadt[7] = ‘\0’; (sind identisch) schreiben? Aufgepasst, die String-Konstante "\0" müsste einem char Array mit der Länge 1 entsprechen. Hier wäre es spannend zu untersuchen, was der Compiler daraus macht.


    Hier ein Auszug aus «Programmieren in C» von Prof. Dr. Rudolf Berrendorf

    Zitat

    Für eine String-Konstante mit n Zeichen legt der Compiler automatisch
    einen Speicherbereich (char-Feld) der Länge
    n+1 an.
    • Die ersten n Zeichen enthalten die Zeichen des angegebenen Strings.
    • Das n+1-te Zeichen ist ein Null-Character (′\0′), das
    vereinbarungsgemäß jeden String in C abschließen muss!
    • So angelegte String-Konstanten dürfen nicht modifiziert wer

    Die letzte Aussage deckt die Problematik hier ab.

    Natürlich dauert dies länger (Kai liegt wieder richtig), zumindest wenn es sich um einen kleinen Chip handelt. Es dauert nicht nur länger, sondern es wird vermutlich auch mehr Code belegen da ja memset() drei Parameter erwartet (diese müssen zuerst in CPU Variablen gefüllt werden) und anschliessend muss der Code der Funktion aufgerufen werden. Bei der reinen Zuweisung wird lediglich der Ziel-Zeiger bestimmt (Zeiger + Indirektion), dann ein Register gelöscht werden und an die Speicherstelle des Zeigers geschrieben werden. Dies sind belegt kaum mehr als 3 Op Codes.



    Ab hier wird es uninteressant ;)

    Der Grund, dass ich oft memset() benutze hat auch wieder Gründe. Ich programmierte sehr viel Software im Bereich der Sicherheit (PC) und eine Variable die ein Passwort oder andere sicherheitsrelevanten Daten speichert, muss man immer nach Gebrauch sofort löschen. Das ist einfacher gesagt als getan, aber «nach Gebrauch» ist schwierig umzusetzen, ausser man verinnerlicht das Tun drum herum. Zusätzlich sollte man sich nicht immer auf den Compiler verlassen, weil die vielfältige Möglichkeiten der Umsetzung von C in den Maschinencode uns nicht sicherer macht. Aus diesem Grund schrieb ich kritische Routinen selber (meist in Assembler), die ich dann blind benutzte.

    Zurück zu memset() und Geschwindigkeit. Die heutigen PC CPU’s sind so komplex geworden, dass es beinahe unmöglich geworden ist, genauere Aussagen zur Ausführgeschwindigkeit zu tätigen. Agner Fog hat beispielsweise herausgefunden, dass das Kopieren eines grösseren Speicherbereichs schneller ablaufen kann, wenn man zuerst einen Kopiervorgang mit memcpy() startet, diesen aber zu Anfang nur einen kleineren Teil der Daten kopieren lässt und danach einen neuen Vorgang startet, der den ganzen Bereich kopiert. Auf den ersten Blick mag dies völlig sinnlos erscheinen, aber er fand heraus, dass die Cache Funktionen und deren interne Umsetzung seltsame Auswirkungen haben können.


    Auf seiner Seite findet man (unter anderem):


    Optimizing software in C++ An optimization guide for Windows, Linux and Mac platforms

    Optimizing subroutines in assembly language: An optimization guide for x86 platforms

    Calling conventions for different C++ compilers and operating systems