umwandlung von char zu int

Hallo allerseits,

ich habe folgendes Problem: ich möchte gerne eine reihe char's die über den Seriellen Monitor an einen Arduino gesendet werden in int's umwandeln.
Dafür habe ich im Internet auch schon gesucht und die Funktion atoi() gefunden. Wenn ich diese jedoch anwende, liefert diese mir nur 0. Frage ist also, was ich falsch geschrieben habe.
Dafür ist hier mein Code:

#include <Adafruit_NeoPixel.h>
#define PIN 2
Adafruit_NeoPixel Feld(400, PIN, NEO_GRB + NEO_KHZ800);
byte preset[11][3] = {
  {0, 0, 0},
  {255, 0, 0},
  {255, 127, 127},
  {255, 255, 0},
  {0, 255, 0},
  {0, 255, 255},
  {0, 0, 255},
  {255, 0, 255},
  {127, 255, 127},
  {127, 127, 255},
  {255, 255, 255}
};

void pixelFarbePreset(int pix, byte param) {
  Feld.setPixelColor(pix, Feld.Color(preset[param][0], preset[param][1], preset[param][2]));
  Feld.show();
}
int led = 12;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Feld.begin();
  Serial.flush();
  pinMode(led, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  String input = "";
  String weltstr = "";
  Feld.clear();
  while (Serial.available() > 0)
  {
    input += (char) Serial.read(); // Read in one char at a time
    delay(5); // Delay for 5 ms so the next char has time to be received
    /*Serial.print(input.length());
      Serial.print("  ");
      Serial.println(input);*/

    if (input.endsWith("#")) {
      //Serial.print("ja");
      input.remove(input.length() - 1);
      int len = input.length();
      if (len == 7) {
        for (int i = 0; i < 7; i++) {
          char chara = input[i];
          int pos = atoi(chara);
          Serial.print(chara);
          Serial.print("  ");
          Serial.println(pos);
          Feld.setPixelColor(i, Feld.Color(preset[pos][0], preset[pos][1], preset[pos][2]));
          Feld.show();
          // pixelFarbePreset(i,pos);
        }

      }
      if ((input == "1") or (input == "2") or (input == "3") or (input == "4") or (input == "5") or (input == "6") or (input == "7") or (input == "8") or (input == "9") or (input == "00") or (input == "10") or (input == "11"))
      {
        Serial.print("[");
        Serial.print(preset[input.toInt()][0]);
        Serial.print("][");
        Serial.print(preset[input.toInt()][1]);
        Serial.print("][");
        Serial.print(preset[input.toInt()][2]);
        Serial.println("]");
      }

      if (input == "on")
      {
        digitalWrite(led, HIGH); // on
      }
      else if (input == "off")
      {
        digitalWrite(led, LOW); // off
      }
    }
  }
}

zur Erläuterung: wenn der Arduino vom PC, noch über den Seriellen Monitor, eine Nachricht, z.b. 1234567# empfängt, soll er diese umwandeln und mittels den im Preset[] gespeicherten RGB-Farbcodes auf einer Reihe WS2812B-Leds ausgeben. Das # am Ende symboliesiert dem Arduino, dass die Nachricht und somit der Zahlencode zu ende ist. das wird gleich im Anfang auch weggestrichen.

Vielen Dank schon mal für die Hilfen

Hallo Alexander,
die Funktion atoi() ist für c-Strings ( = \0 terminierte char-Arrays ) geschrieben. Sie wandelt auch nicht einen einzelnen char, sondern eine komplette Zahl in einem c-string - also z.B. "1234" in den entsprechenden Integerwert. Als Parameter erwartet sie also die Adresse eines char-Arrays.

Der Datentyp 'String' ist sehr speicherfressend, und den sollte man auf den kleinen Arduinos eigentlich nicht verwenden. Ich würde dir empfehlen, auf c-Strings umzusteigen, und dann atoi() zur Umwandlung zu verwenden.

was ist denn mit "Adresse" gemeint?

und was muss ich dann da reinschreiben? Ist heute das erste mal, dass ich von "Adresse" höre...

liebe Grüße, Alex

20Alexanderxx:
was ist denn mit "Adresse" gemeint?

Die Adresse "zeigt" auf einen Ort im Speicher.
Jeder Speicherort hat eine Adresse.

Wie combie sagt, die Speicherstelle, wo der c-string abgelegt ist.

char meineZahl[] = "12345"; // das ist ein c-string
int alsInteger;
...
alsInteger = atoi(meineZahl); // alsInteger enthält jetzt den Wert 12345

Edit: Wenn Du ein Array anlegst, ist der Arrayname die Adresse, wo das Array im Speicher abgelegt ist.

nochmal als nebeninfo: später möchte ich eine 400er Zeichenkette an den Arduino übergeben.

was mich also interessiert ist die Möglichkeit, einzelne Elemente aus einem String in int werte umzuwandeln. Also andere Methoden als atoi

20Alexanderxx:
nochmal als nebeninfo: später möchte ich eine 400er Zeichenkette an den Arduino übergeben.

Dann vergiss den Datentyp 'String' auf jeden Fall ganz schnell wieder. ( Um was für einen Arduino handelt es sich denn?).

Grundsätzlich kannst Du mit atoi nicht nur eine Zahl am Anfang der Zeichenkette umwandeln. Du kannst ihm ja eine beliebige 'Adresse :wink: ' mittendrin übergeben - wo halt deine Zahl anfängt.
Edit: wenn du im obigen Beispiel schreibst:
alsInteger = atoi(meineZahl+2); käme 345 als Ergebnis raus.

Um da genauere Tips zu geben, müsste man aber wissen, was da genau ankommt. Kommen die 400 Zeichen tatsächlich in einem 'Rutsch' bevor Du anfängst da was auszuwerten?
Dann gibt es auch noch 'strtok' um strings zu zerlegen.

Hallo,

auch wenn es ein wenig frech wirkt, aber die Frage lässt nur eine Antwort zu. :grin:

char c = 127;

void setup(void)
{
  Serial.begin(9600);
  int var = static_cast<int>(c);  // Datentypkonvertierung
  Serial.println(var);  
}

void loop(void)
{

}

MicroBahner:
Dann vergiss den Datentyp 'String' auf jeden Fall ganz schnell wieder. ( Um was für einen Arduino handelt es sich denn?).

Grundsätzlich kannst Du mit atoi nicht nur eine Zahl am Anfang der Zeichenkette umwandeln. Du kannst ihm ja eine beliebige 'Adresse :wink: ' mittendrin übergeben - wo halt deine Zahl anfängt.
Edit: wenn du im obigen Beispiel schreibst:

alsInteger = atoi(meineZahl+2);

käme 345 als Ergebnis raus.

Um da genauere Tips zu geben, müsste man aber wissen, was da genau ankommt. Kommen die 400 Zeichen tatsächlich in einem 'Rutsch' bevor Du anfängst da was auszuwerten?
Dann gibt es auch noch 'strtok' um strings zu zerlegen.

also es wird am ende ein arduino Nano, momentan ist es aber zu testzwecken ein arduino mega.
Um es jetzt nochmal groß zu fassen, die Serielle Kommunikation soll als Brücke zwischen einem Lazarus/Pascal-Programm und dem Arduino zur Hardware-steuerung dienen. dazu gehört unter anderem eine 20x20 WS2812B LED-Wand und Motoren. Damit die LED-Wand am Anfang auch weiß, was diese anzeigen soll, habe ich halt am Anfang das Preset erstellt, wo ich also am Ende nur in meine Routine pixelFarbePreset(pixel, presetnummer(z.b.3)) eingeben brauche.
Wie man schon erahnen kann, sollen diese Parameter über die Serielle verbindung übergeben werden. Kommt also eine 9 an, wird an erster stelle der LED-Wand das preset 9 angezeigt. Kommt über die ser.Komm etwa 987 an, so wird bei der led1 Preset 9 angezeigt, bei Led2 preset 8 und bei led3 preset 7. dies soll aber nach Möglichkeit direkt und ohne zwischenspeichern auf die Wand übertragen werden, also quasi rein und wieder raus. von dem lazarus Programm weiß ich schon, dass die Infos char für char verschickt werden. Eigentlich doch optimal, oder?

Was mir jetzt noch beim versuchen aufgefallen ist, ist die Tatsache, dass ab einer bestimmten Menge an chars irgendwann nichts mehr gesendet wird.
Also war jetzt meine Idee, um den String möglichst kurz zu halten, dass ich immer dass erste Element entferne und somit der input-String immer die länge eins hat. Dafür habe ich schon folgendes Programm entworfen:

#include <Adafruit_NeoPixel.h>
#define PIN 2
Adafruit_NeoPixel Feld(400, PIN, NEO_GRB + NEO_KHZ800);
byte halb = 60;
byte preset[11][3] = {
  {0, 0, 0},
  {255, 0, 0},
  {255, halb, halb},
  {255, 255, 0},
  {0, 255, 0},
  {0, 255, 255},
  {0, 0, 255},
  {255, 0, 255},
  {halb, 255, halb},
  {halb, halb, 255},
  {255, 255, 255}
};
byte y, x, richtung;

void pixelFarbePreset(int pix, byte param) {
  Feld.setPixelColor(pix, Feld.Color(preset[param][0], preset[param][1], preset[param][2]));
  Feld.show();
}
int led = 12;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Feld.begin();
  Serial.flush();
  pinMode(led, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  richtung = richtung % 4;
  String input = "";
  int anzKisten;
  char charArray[410];
  Feld.clear();
  int counter = 0;

  while (Serial.available() > 0)
  {
    input += (char) Serial.read();
    delay(5); // Delay for 5 ms so the next char has time to be received
    charArray[counter] = input[input.length() - 1];
    //pixelFarbePreset(counter, charArray[counter]);
    counter++;
    Feld.setBrightness(20);
    Serial.print(counter);
    Serial.print("  ");
    Serial.print(input);
    Serial.print("  ");
    Serial.println(charArray[counter]);

    input.remove(input.length()-1);
  }
}

gibt es da noch elegantere Lösungen oder wie muss ich es modifizieren, damit es endlich funktioniert?
und noch ne Frage: wenn ich bei pixelFarbePreset() im loop() die // vorne noch entferne kommt plötzlich nicht mehr das an, was ich im seriellen Monitor losschicke. Woran liegt das?

viel Spaß schon mal beim zerlegen :wink: aber ein herzliches Danke von mir

EDIT: uups, doch so viel text geworden :smiley:

Dass String der falsche Weg ist, wurde Dir ja schon gesagt. nDu solltest es beherzigen.

Auch das Senden der Einzelinformationen ist fehlerträchtig, da Du keinerlei Synchronisationsinformationen hast.
Besser wäre es, wenn Du immer die Infos für eine ganze Zeile sendest und diese mit einem NewLine ('\n') abschließt.

Was verstehst Du unter einem Preset?

Gruß Tommy

ich muss ehrlich dazu sagen, dass ich es durchaus mit ohne String versucht habe, das hat bei mir dann im Code für noch mehr Fehler geführt, weshalb ich dann ganz schnell Str+Z gedrückt habe.

das mit dem NewLine verstehe ich noch nicht so ganz. Ich hatte zwischenzeitlich die Methode, immer mit einem # abzuschließen, damit wusste der Arduino dann, dass es jetzt zu ende ist. Dachte aber, dass dies überflüssig ist.

Presets sind bei mir die RGB-Codes, abgespeichert in einem 11x3 Array von bytes, damit ich später einfach die Routine pixelFarbePreset(int pix, byte param) nutzen kann, steht im code fast ganz oben;) das erleichtert mir an einigen Stellen das Arbeiten...

Bei Fehlern solltest Du nicht zurückschrecken, sondern die Ursache des Fehlers ermitteln und bseitigen.
Dein # würde auch die Funktion als Endezeichen erfüllen.

Gruß Tommy

ok, danke, das werde ich dann auch mal versuchen.
Nochmal also zur Wiederholung: statt eines Strings soll ich also ein char-Array erstellen? Soll dies dann auch schon von der größe festgelegt sein oder dynamisch?

und gibt es eine Grenze, wie viele der Serielle Monitor maximal senden kann?

20Alexanderxx:
das mit dem NewLine verstehe ich noch nicht so ganz.

Was ist daran nicht verständlich? Das Linefeed ist ein Steuerzeichen das man automatisch senden kann. println() macht automatisch CR + LF. Im seriellen Monitor kann man einstellen dass es automatisch angehängt wird wenn man den Text absendet. Das ist viel bequemer als ein sichtbares Endzeichen

Hier ist eine Funktion die Text nicht-blockierend einliest:
https://forum.arduino.cc/index.php?topic=659446.msg4443056#msg4443056

loop() kann dann einfach so aussehen:

void loop()
{
  char* str = readLine(Serial);
  if (str != nullptr)
  {
      unsigned int value = atoi(str);

      ....
  }
}

Oder man kann Text einlesen:

void loop()
{
  char* str = readLine(Serial);
  if (str != nullptr)
  {
      if (strcmp(str, "an") == 0)
         ...
      else if (strcmp(str, "aus") == 0)
         ...
  }
}

20Alexanderxx:
ok, danke, das werde ich dann auch mal versuchen.
Nochmal also zur Wiederholung: statt eines Strings soll ich also ein char-Array erstellen? Soll dies dann auch schon von der größe festgelegt sein oder dynamisch?

Auf den kleinen MC immer statisch. Überlege Dir eine sinnvolle Länge, die Du am Stück senden willst, das Array 1 Zeichen größer. Anstelle des # schreibst Du dort einfach '\0' als Abschluß rein, da kannst Du es Dir im seriellen Monitor ausgeben lassen.
Gültige Zeichen sind nur '0'...'9' und '#'?

Gruß Tommy

und ich nehme an, das nullptr ist dieser zeilenumbruch?
und wofür steht das strcmp?

Es wäre besser, wenn Du von irrigen Annahmen weg kommst und mal langsam anfängst ein paar Grundlagen zu lernen.

Gruß Tommy

Du findest mit Google massig Seiten die solche Dinge erklären

http://www.cplusplus.com/reference/cstring/strcmp/

nullptr steht für Null Pointer. Die verlinkte Funktion hat doch Kommentare die das erklärt. Von dem Linefeed bekommst du außen nichts mehr mit, da es gar nicht abgespeichert wird. nullptr kommt zurück wenn der String noch nicht fertig eingelesen ist. Wenn man fertig ist bekommt man einen Zeiger auf das Array und der ist natürlich ungleich Null

Das mit nullptr hängt erstmal mit Serenifly's Liebling readLine zusammen.
Diese tolle Funktion braucht nämlich selbst keine Zeit und kommt immer sofort zurück. Meistens mit der Info, dass keine komplette Zeile da ist, nur manchmal mit der ganzen Zeile als char* . Da es nun Zeilen mit Text und Leerzeilen ohne Text gibt (die auch als solche gemeldet werden), braucht es noch einen Sonderfall für "noch nicht fertig". Das ist nun dieser ungültige Zeiger nullptr.

strcmp kannst du aber wirklich selber googeln und damit experimentieren.

ok, vielen dank, jetzt funktionierts. :smiley:

mal noch ne kleine Nebenfrage, ich arbeite nur am Notebook und nutze immer die scroll-Funktion des Touchpads. Doch manchmal scrollt die Arduino IDE nicht mit, dann muss ich immer an dem "Gewicht" ziehen. Kommt das bei euch auch vor oder liegt es mal wieder an mir?