An Array werte anhängen

Hallo miteinander,

ich habe jetzt über die Serielle Schnittstelle die Möglichkeit meinen Arduino Mega Daten mitzuteilen, ich möchte das die Daten in ein Array geschrieben bzw. ans ende angehängt werden.

hat mir jemand eine Idee?

ich habe hier mein Kleines Test Programm

wenn man hier 3 Zahlen durch was auch immer getränt ich mache immer ein Lesezeichen über die Serielle Schnittstelle Sendet dann sollen die Werte an das bestehende Array angehängt werden und das vollständige Array ausgegeben werden.

irgend wie verstehe ich davon nur nicht all zu viel :frowning:
würde mich über etwas Unterstützung sehr freuen.

Gruß Mücke

hier ist der Code:

struct hardware_t{int pin;long vorlaufzeit; long dauer;};

// Arrey mit Ausführbaren Aktionen bestücken
 hardware_t hardware[]=                                 
    {
      {10, 20, 30},
      {11, 21, 31},
    };

void setup() 
{
  // initialize serial:
  Serial.begin(9600);
}

void loop() 
{
  // wenn was vom Seriellerschnittstelle kommt dann soll es gelesen werden:
  while (Serial.available() > 1)
  {
  
    // nach dem nächsten int suchen(mann kann werte mit space komma etc. trennen):
    int PIN         = Serial.parseInt();
    int VORLAUFZEIT = Serial.parseInt();
    int DAUER       = Serial.parseInt();

// Arrey erweitern    ?? Leider ohne ervolg :-( 
  hardware_t hardware[]=
    {
      {PIN, VORLAUFZEIT, DAUER},
    };

  // Hadware Arrey ausgeben volständig   
   Serial.println  ("hardware[] - Ausgabe");
    int schritte = sizeof(hardware) / sizeof(hardware[0]);                // Anzahl an eintragungen auslesen
    for (int i=0;i<schritte;i++)
    {
      Serial.print(hardware[i].pin);
      Serial.print(", ");
      Serial.print(hardware[i].vorlaufzeit);
      Serial.print(", ");
      Serial.println(hardware[i].dauer);
    }
  
  }
}

in der zeile

// Arrey erweitern    ?? Leider ohne ervolg :-( 
  hardware_t hardware[]=
    {
      {PIN, VORLAUFZEIT, DAUER},
    };

habe ich versucht den neuen erhaltenen Datensatz an das Array zu hängen geht nur so nicht wie ich das gemacht habe :frowning: schade.

EDIT:
ich habe auch schon versucht in die Geschweifte Klammer eine For schleife einzubauen so das ich das bisherige Array aufgerufen hatte und die Werte neu rein geschrieben, doch bei der Fort schleife bekomm ich immer eine Fehlermeldung :frowning:

Wenn man nicht dynamischen Speicher verwendet, muss die Größe von Arrays zur Compile-Zeit feststehen! Man kann statische Arrays nicht einfach erweitern.

Hast du eine maximale Größe an Datensätzen? Dann mach dein Array entsprechend groß und fülle es schrittweise. Das ist zwar auf den ersten Blick ineffizient was die Speicherbelastung betrifft, aber dynamischer Speicher bringt seine eigenen Probleme mit. Außerdem hast du auf dem Mega genug.
Du kannst auch etwas optimieren. PIN muss z.B. kein int sein. Ein Byte reicht da.

vom Prinzip her weis ich wie Groß mein Array sein muss wenn ich das über die Serielle Schnittstelle mache,
dann muss ich nur in die Übertragung die Gesamt Größe des Array`s mit eintragen.

wie würde das denn dann aussehen wenn ich das nach und nach Füllen möchte?

die Deklaration müsste dann also so aussehen?

// Array mit Ausführbaren Aktionen bestücken
 hardware_t hardware[2]=                                 
    {
      {10, 20, 30},
      {11, 21, 31},
    };

die Zwei (2) ist dann die gesamt Größe?
oder habe ich das Falsch verstanden?

an genommen ich würde anstelle der Zwei eine Drei nehmen dann könnte ich noch einen Datensatz anhängen:
was ich dann so machen würde:

  hardware_t hardware[3]=                                 
    {
      {12, 22, 32},
    };

irgend wie stimmt da was nicht glaube ich, ich glaube ich Deklariere das Array in der Größe Falsch? oder?

Ja, das ist die gesamte Größe. Wenn man die Initialisierungs-Klammern verwendet, kann der Compiler aus der Anzahl der Werte ableiten wie groß das Array sein muss. Aber du kannst auch ein Array der Größe 10 erstellen und nur 2 davon initialisieren. Der Rest ist dann 0 da global. Aber der Speicher ist halt reserviert und kann dann später beschrieben werden.

Du sollte dabei aber unbedingt überprüfen, dass du nicht über die Array-Grenze hinausschreibst. Sonst kannst du z.B. 20 Werte senden, obwohl nur Platz für 10 sind. Und der speichert die auch, aber überschreibt andere Variablen.

Ok, irgend wo ist noch der Wurm drin, etwas mache ich Falsch.

ich nehme für das fest Programm an das ich Drei werte drin stehen haben also schreibe ich in die Eckige Klammer die Drei rein.

dann Fülle ich es mit zwei Werten.

wie Füge ich jetzt den Dritten wert der über die Serielle Schnittstelle kommt bei mir im Test Programm noch mit hinzu?

so wie ich es jetzt gemacht habe steht der Wert am Anfang und der Rest hat alles nullen :frowning:

irgend wie nicht so wie ich mir das vorgestellt habe.

struct hardware_t{int pin;long vorlaufzeit; long dauer;};

// Arrey mit Ausführbaren Aktionen bestücken
 hardware_t hardware[3]=                                 
    {
      {10, 20, 30},
      {11, 21, 31},
    };

void setup() 
{
  // initialize serial:
  Serial.begin(9600);
}

void loop() 
{
  // wenn was vom Seriellerschnittstelle kommt dann soll es gelesen werden:
  while (Serial.available() > 1)
  {
  
    // nach dem nächsten int suchen(mann kann werte mit space komma etc. trennen):
    int PIN         = Serial.parseInt();
    int VORLAUFZEIT = Serial.parseInt();
    int DAUER       = Serial.parseInt();

// Arrey erweitern    ?? Leider ohne ervolg :-( 
  hardware_t hardware[3]=
    {
      {PIN, VORLAUFZEIT, DAUER},
    };

  // Hadware Arrey ausgeben volständig   
   Serial.println  ("hardware[] - Ausgabe");
    int schritte = sizeof(hardware) / sizeof(hardware[0]);                // Anzahl an eintragungen auslesen
    for (int i=0;i<schritte;i++)
    {
      Serial.print(hardware[i].pin);
      Serial.print(", ");
      Serial.print(hardware[i].vorlaufzeit);
      Serial.print(", ");
      Serial.println(hardware[i].dauer);
    }
  
  }
}

OK, ich dachte mir das ich an meinen Seriellen Daten noch zwei weitere Zahlen anhänge ein mal den Wert wo der Inhalt hin soll und einen für die gesamt Größe des Array`s.

wie bekomme ich die Größe als Variable in die Eckige Klammer?
und dann ist noch das Problem wie kann ich das dann Zeilenweise Füllen?

das ist mein Code:

struct hardware_t{int pin;long vorlaufzeit; long dauer;};

void setup() 
{
  // initialize serial:
  Serial.begin(9600);
}

void loop() 
{
  // wenn was vom Seriellerschnittstelle kommt dann soll es gelesen werden:
  while (Serial.available() > 1)
  {
  
    // nach dem nächsten int suchen(mann kann werte mit space komma etc. trennen):
    int PIN         = Serial.parseInt();
    int VORLAUFZEIT = Serial.parseInt();
    int DAUER       = Serial.parseInt();
    int ZEILE       = Serial.parseInt();
    int GROESSE     = Serial.parseInt();
    

// Arrey erweitern    ?? Leider ohne ervolg :-( 
  hardware_t hardware[GROESSE]=
    {
      {PIN, VORLAUFZEIT, DAUER},
    };

  // Hadware Arrey ausgeben volständig   
   Serial.println  ("hardware[] - Ausgabe");
    int schritte = sizeof(hardware) / sizeof(hardware[0]);                // Anzahl an eintragungen auslesen
    for (int i=0;i<schritte;i++)
    {
      Serial.print(hardware[i].pin);
      Serial.print(", ");
      Serial.print(hardware[i].vorlaufzeit);
      Serial.print(", ");
      Serial.println(hardware[i].dauer);
    }
  
  }
}

Das ist generell Pfusch. Du musst dein Array global deklarieren, sonst hört es am Ende von loop() auf zu existieren. Einfach so:

hardware_t hardware[GROESSE];

Keine Klammern. Nur ein leeres Array. Außerhalb von setup() und loop()

GROESSE muss zu Compile-Zeit feststehen. Du kannst den Wert nicht über Serial einlesen!

Mich wundert es dass du da so große Problem hast, da du das Array doch bei der Serial Ausgabe korrekt ansprichst. Genauso kannst du Werte zuweisen:

hardware[zeile].vorlaufzeit = 5

Dein Serial Code ist aber auch sehr mangelhaft. Du musst schon warten bis alles da ist. Bei 9600 Baud dauert ein Zeichen 1ms! Du kannst daher nicht schon nach dem ersten Zeichen alles parsen. Die Daten sind noch auf der Leitung!

Ich würde erst mal die Baudrate auf 115200 setzen. Ganz primitiv kannst du dann sowas machen:

if(Serial.available())
{
    delay(300);          //entsprechend groß machen, dass Zeit für alle Zeichen ist! 300ms sind hier glaube ich schon zu viel
 
    int PIN = Serial.parseInt();
    int VORLAUFZEIT = Serial.parseInt();
    int DAUER = Serial.parseInt();
    int ZEILE = Serial.parseInt();
}

Das wartet nach dem ersten Zeichen einfach bis alles da ist und liest erst dann ein. Es gibt aber bessere Lösungen.

Großbuchstaben verwendend man übrigens i.d.R. nur für Konstanten

Trenne das Problem erst mal auf. Mach erst mal nur Serial und schreibe die gelesenen Werte auf den Serial Minitor zurück.
Erst wenn das geht, versuche die Werte in das Array zu schreiben.

das mit dem Pfusch, glaube ich dir
ich Spreche das Array richtig an bei der Ausgabe da ich das aus einem Anden Code raus genommen habe.
Ich habe von der Materie nicht sonderlich viel Hintergrund wissen und Baue mir alles aus anderen Codes zusammen und versuche es dann zu verstehen.

die Vorgehensweise, erst die Seriellen Daten zu Testen und dann die wider zurück geben das habe ich schon probiert über den Punkt bin ich schon, bisher hat das immer ohne Probleme geklappt.

deinen Code zu der Seriellen Übertragung werde ich gleich mir genauer anschauen:

ich habe jedoch ein Problem, denn ich kenne die Größe zwar jedoch nur dann wenn die Firmware schon auf dem Bord ist, ich Sende immer unterschiedliche Setup Daten für eine Hardware Ansteuerung an das Bord, und die kann man nur aus 2 Zeilen oder auch aus 100 Zeilen bestehen, und wenn es Doof läuft auch aus 500 Zeilen.
Daher dachte ich das ich das über die Seriell Schnittstelle mit übertrage.
Hmm das ist jetzt schlecht für mich. denn ich arbeite in meinem Anderen Programm schon mit dieser Datenstruktur und würde die nur sehr ungern umschreiben müssen. ich arbeite ins besondere damit das ich auslese wie groß das Array ist und las die Hardware dann damit steuern.

Welche Möglichkeit würde es denn noch geben?

ps. das mit der Großschreibung werde ich mir merken :slight_smile:

Ok, wenn der Serial Code geht, dann kannst du es auch lassen. Ich sehe jetzt auch wieso das wahrscheinlich geht:

parseInt() wartet anscheinend bis was da ist.

Man kann Speicher auch dynamisch mit malloc() oder calloc() anlegen und mit free() wieder freigeben. Damit kann man die Größe von Arrays erst zur Laufzeit festlegen:

(das Beispiel geht sogar grob in die Richtung die du willst)

Oder man kann realloc() machen die Größe von bestehenden Arrays zu verändern (sofern diese vorher mit malloc() angelegt wurden). Wenn du aber schon mit statischen Arrays Problemen hast, kannst du dadurch noch mehr verzweifeln. Das ist fehleranfällig wenn man nicht aufpasst was man macht :slight_smile:

Du hast auf dem Mega aber auch 8kB RAM. Bei einem Byte für den Pin (kein int!) und zwei ints hast du 1 + 2 + 2 = 5 Bytes pro Datensatz. Ein Array mit 500 Elementen belegt daher 2,5kB. Das wäre eventuell auch statisch vertretbar, auch wenn du den Speicher nicht vollständig verwendest.

EDIT: Du hast vorlaufzeit und dauer als long definiert. mit byte + long + long hast du 1 + 4 + 4 = 9 Bytes. Wären 4,5kB für 500 Werte. Aber den Speicher brauchst du so oder so. Auch mit malloc() im worst-case.

Du liest allerdings mit parseInt() ein. Die Funktion gibt einen int zurück. Longs kann man damit anscheinend gar nicht lesen.

ich habe das jetzt mal umgesetzt was aus deinem Beitrag vorher noch übrig war:

struct hardware_t{int pin;long vorlaufzeit; long dauer;};
  hardware_t hardware[3];
  
void setup() 
{
  // initialize serial:
  Serial.begin(9600);
}

void loop() 
{
  // wenn was vom Seriellerschnittstelle kommt dann soll es gelesen werden:
  while (Serial.available() > 1)
  {
  
    // nach dem nächsten int suchen(mann kann werte mit space komma etc. trennen):
    int PIN         = Serial.parseInt();
    int VORLAUFZEIT = Serial.parseInt();
    int DAUER       = Serial.parseInt();
    int Zeile       = Serial.parseInt();

// Arrey erweitern    ?? Leider ohne ervolg :-( 
      hardware[Zeile].pin         = PIN;
      hardware[Zeile].vorlaufzeit = VORLAUFZEIT;
      hardware[Zeile].dauer       = DAUER;

  // Hadware Arrey ausgeben volständig   
   Serial.println  ("hardware[] - Ausgabe");
    int schritte = sizeof(hardware) / sizeof(hardware[0]);                // Anzahl an eintragungen auslesen
    for (int i=0;i<schritte;i++)
    {
      Serial.print(hardware[i].pin);
      Serial.print(", ");
      Serial.print(hardware[i].vorlaufzeit);
      Serial.print(", ");
      Serial.println(hardware[i].dauer);
    }
   }
}

wenn ich jetzt die Größe von beginn her kennen würde dann könnte ich zumindest mal das Array füllen :-).

Was ist nicht verstehe ist wenn ich anstelle von GROESSE eine Zahl eintrage dann klapp es.
also das Array zu erzeugen Ohne eine Fehlermeldung und auch in der definierten Größe, nur mit der Variablen geht das nicht :frowning:
das verwirrt mich etwas.

Die Variablen sind so Definiert da ich die Eingabe in Millisekunden mache jedoch in meinem anderen Programm auf "Mikrosekunden" umrechne um die Hardware richtig ansprechen zu können.

Serenifly:
Ok, wenn der Serial Code geht, dann kannst du es auch lassen. Ich sehe jetzt auch wieso das wahrscheinlich geht:
Serial.parseInt() - Arduino Reference
parseInt() wartet anscheinend bis was da ist.

puh das bereuet mich :slight_smile: denn ich heb den Code auch nur woanders raus kopiert :slight_smile:

Serenifly:
Man kann Speicher auch dynamisch mit malloc() oder calloc() anlegen und mit free() wieder freigeben. Damit kann man die Größe von Arrays erst zur Laufzeit festlegen:
http://www.elektronik-bastelkeller.de/C_Ardu_8_4.php
(das Beispiel geht sogar grob in die Richtung die du willst)

Oder man kann realloc() machen die Größe von bestehenden Arrays zu verändern (sofern diese vorher mit malloc() angelegt wurden). Wenn du aber schon mit statischen Arrays Problemen hast, kannst du dadurch noch mehr verzweifeln. Das ist fehleranfällig wenn man nicht aufpasst was man macht :slight_smile:

Hmm das siht Komplex aus, ... sch...

Serenifly:
Du hast auf dem Mega aber auch 8kB RAM. Bei einem Byte für den Pin (kein int!) und zwei ints hast du 1 + 2 + 2 = 5 Bytes pro Datensatz. Ein Array mit 500 Elementen belegt daher 2,5kB. Das wäre eventuell auch statisch vertretbar, auch wenn du den Speicher nicht vollständig verwendest.

EDIT: Du hast dauer als long definiert, aber liest nur mit parseInt() ein. Long macht hier Sinn. mit byte + int + long hast du 1 + 2 + 4 = 7 Bytes. Wären 3,5kB für 500 Werte. Aber den Speicher brauchst du so oder so. Auch mit malloc() im worst-case.

dann bin ich ja froh das ich mich für das Mega endschiden habe beim kauf.
ich werde das mal testen was pasirt wenn ich lauter 0 in meinem Array habe bei der Ansteuerung der Hadware.

mit der Aktuellen Strucktur habe ich:

struct hardware_t{int pin;long vorlaufzeit; long dauer;};

Pin = 2 Bytes
vorlaufzeit = 4 Bytes
dauer = 4 Bytes

Pro Datensatz also 2 + 4 + 4 = 10 Bytes
bei 500 Datensätzen wären das 5kB
da solten die 8kB RAM auf dem Arduino Mega ausreichen? oder muss ich das Programm noch mit einrechnen? oder ligt das wo anderst?

Wie gesagt: Die Größe von statischen Arrays muss beim Kompilieren feststehen! Mann mit int test keine Arrays zur Laufzeit erstellen.

Wenn du die Größe erst zur Laufzeit festlegst, brauchst du malloc(). Und dann muss dir klar sein, dass sich dynamischer Speicher völlig anders verhält als wenn du die Arrays einfach beim Kompilieren erstellst.

Ja, mit dem Speicher bin ich etwas durcheinander gekommen.
Wenn du für Pin ein Byte verwendest brauchst du etwas weniger. Sowas spielt hier eine Rolle. Ein int hat zwei Bytes. Da belegst du gleich mal ein paar hundert Bytes, die du gar nicht brauchst. Es sei denn deine Pin Nummern sind größer als 255.

Bei int/long hatte ich einen Fehler gemacht. Aber byte statt int für die Pins sollte eine Empfehlung sein, da ich dachte, dass du für Pins keine größeren Werte brauchst.

das mit dem Pin werde ich schauen denn der Andere Code ist auch nicht zu 100% von mir :slight_smile: und ich muss mal schauen ob ich das umändern kann :-).

Wenn das dann noch klappt das es nicht stört das ich lauter 0 Werte im Array habe würde das Programm jetzt laufen :slight_smile: bin schon gespant, muss das ausgiebig jetzt mal Testen.

Danke erst mal für deine Geduld und deine Hilfe, werde mich dann melden wenn es läuft oder auch nicht :wink:
und die Resultate dann hier Pressestimmen, das sollen schöne Bilder werden. (TaT „Tropfen in Tropfen“ Bilder)

ps. das mit dem gesamten Speicher sollte hin hauen? oder ist da jetzt noch ein Fehler drin? als wenn ich den Ram mit 5kB belege? muss ich dann das Programm auch noch in den Ran mit einrechnen oder ist der RAM 8kB auf dem Mega nur für Variablen

Ja, das sollte reichen. Auch wenn du 5k belegst. Zumindest solange du sonst nichts hast das groß RAM frisst. Also z.B. auf die String Klasse verzichten.

Hiermit kannst du dir den freien Speicher anzeigen lassen:

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

Wenn du dynamischen Speicher hast funktioniert das aber u.U. nicht mehr korrekt

Im Zweifelsfall gibt es immer noch externe RAMs für SPI oder I2C :slight_smile:
Du muss man die Daten halt per Hand reinschaufeln, da die nicht Teil des normalen Adressraums sind

mal ne Blöde Frage,
wie Lösche ich das Arrey denn volständig, also sätze alle werte auf 0 ?

gibt es da einen Befehl für? oder muss ich eine Schleife duchlaufen lassen das alles auf 0 sätzt?

Vorsicht mit den Begriffen. Ich weiß zwar was du meinst, aber statische Arrays kannst du nicht wirklich löschen. Der Speicher ist immer belegt.

Um Arrays auf 0 zu setzen gibt es memset:
http://www.cplusplus.com/reference/cstring/memset/

Der 1. Parameter ist das Array. Der 2. ist der Wert. Der 3. ist die Größe in Bytes. Also:

memset(hardware, 0, sizeof(hardware));

Wobei globale Variablen immer auf 0 initialisiert werden. Beim Start sollte also alles schon 0 sein

Ok du hast recht Löschen ist der Falsche Ausdruck, besser wäre leeren :-).
denn der Speicher ist ja immer noch Reserviert und das Array kann dann wider neu gefüllt werden :slight_smile:

ich habe das so gemacht:

    int schritte = sizeof(hardware) / sizeof(hardware[0]);                // Anzahl an eintragungen auslesen
    for (int i=0;i<schritte;i++)
    {
      hardware[i].pin         = 0;
      hardware[i].vorlaufzeit = 0;
      hardware[i].dauer       = 0;
    }

Dein Code gefällt mir viel besser ist Kleiner und wahrscheinlich auch noch Schneller :slight_smile: