Forum: Compiler & IDEs Struct Arduino dynamisches Array verarbeiten


von structeur (Gast)


Lesenswert?

Guten Morgen,

ich habe eine Verständnisfrage zu einem Fehler während der 
Arduino-Kompilierung.
1
const int SERVO_IDENTIFIER_BOX = 1;
2
const int SERVO_IDENTIFIER_ARM = 2;
3
4
const bool SERVO_DIRECTION_LOOP_POSITIVE = true;
5
const bool SERVO_DIRECTION_LOOP_NEGATIVE = false;
6
7
const bool SERVO_DIRECTION_BOX_UP = SERVO_DIRECTION_LOOP_POSITIVE;
8
const bool SERVO_DIRECTION_BOX_DOWN = SERVO_DIRECTION_LOOP_NEGATIVE;
9
const bool SERVO_DIRECTION_ARM_UP = SERVO_DIRECTION_LOOP_POSITIVE;
10
const bool SERVO_DIRECTION_ARM_DOWN = SERVO_DIRECTION_LOOP_NEGATIVE;
11
12
const int MAX_SERVO_ARM_RADIUS = 370;
13
const int MAX_SERVO_BOX_RADIUS = 100;
14
15
/**
16
 * 
17
 * 
18
 * address = gibt den Servo an, der angesprochen werden soll
19
 * pos_begin = aus welchem Winkel der Servoarm begonnen werden soll 
20
 * pos_stop = bei welchem Winkel der Servoarm seinen Maximalpunkt erreicht haben soll
21
 * delay_loop = Pause in Millisekunden innerhalb einer Schleife (Start-Endpunkt)
22
 * move_direction = Gibt an, ob die Schleife auf- oder abwärts durchlaufen werden soll.
23
 */
24
25
typedef struct {
26
    int address;
27
    int pos_begin;
28
    int pos_stop;
29
    int delay_loop;
30
    bool move_direction ;
31
    int optional_repeat ;
32
    int optional_repeat_delay ;
33
} ServoLoop;
34
35
ServoLoop actionOne[] = {
36
        {SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 200, SERVO_DIRECTION_BOX_UP},
37
        {SERVO_IDENTIFIER_ARM, 0, MAX_SERVO_ARM_RADIUS, 200, SERVO_DIRECTION_ARM_UP},
38
        {SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 200, SERVO_DIRECTION_BOX_DOWN},
39
        {SERVO_IDENTIFIER_ARM, 0, MAX_SERVO_ARM_RADIUS, 200, SERVO_DIRECTION_BOX_DOWN}
40
};
41
42
void setup() {
43
  Serial.begin(9600);
44
}
45
46
void loop() {
47
 executeAction(actionOne);
48
 delay(1000);
49
}
50
51
void executeAction(ServoLoop action[]) {
52
  Serial.println("ACTION_BEGINN");
53
  
54
  const int countElements = sizeof(action) / sizeof(ServoLoop);
55
  Serial.println("Count of elements: " + countElements);
56
  for (int i = 0; i < countElements; i++) {
57
    Serial.println("Action Element: " + i);
58
    Serial.println(action[i].address);
59
    delay(300);
60
  }
61
62
  Serial.println("ACTION_FINISHED");
63
}

Hier ist die Ausgabe lediglich
1
ACTION_BEGINN
2
Count of elements: 
3
ACTION_FINISHED
4
ACTION_BEGINN
5
Count of elements: 
6
ACTION_FINISHED
7
ACTION_BEGINN
8
Count of elements: 
9
ACTION_FINISHED

Würde ich den Code im Loop wie folgt ausführen, käme das gewünschte 
Verhalten zum Vorschein.
1
void loop() {
2
  Serial.println("L_BEGINN");
3
  const int countElements = sizeof(actionOne)/sizeof(ServoLoop);
4
  for (int i = 0; i < countElements; i++) {
5
    Serial.println(actionOne[i].address);
6
    delay(300);
7
  }
8
  Serial.println("L STOP");
9
}

Mit der Ausgabe
1
L_BEGINN
2
1
3
2
4
1
5
2
6
L STOP
7
L_BEGINN
8
1
9
2
10
1
11
2
12
L STOP
13
....

Mein Ziel:
Ich möchte mehrere "Actions" frei definieren, und diese Aktion als 
Parameter einer Methode übergeben und dort "Lokal" verarbeiten.

Ich bin mir dessen bewusst, dass ich bei der Übergabe der Variable 
irgendwie was falsch mache, weil ich irgendwas Grundlegendes 
offensichtlich nicht verstanden habe.

Mein zweiter Versuch geht in die Richtung:
1
executeAction(&actionOne);
2
3
// und als funktion
4
5
void executeAction(ServoLoop *action[]) {}

Doch dann erhalte ich vom Kompiler:
1
cannot convert 'ServoLoop (*)[4]' to 'ServoLoop**' for argument '1' to 'void executeAction(ServoLoop**)'

Was konkret habe ich noch nicht verstanden und wie müsste es korrekt 
sein?

Eine zweite Aktion könnte sein
1
ServoLoop actionTwo[] = {
2
        {SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 200, SERVO_DIRECTION_BOX_UP},
3
        {SERVO_IDENTIFIER_ARM, 0, 50, 200, SERVO_DIRECTION_ARM_UP},
4
        {SERVO_IDENTIFIER_ARM, 0, MAX_SERVO_ARM_RADIUS, 200, SERVO_DIRECTION_ARM_UP},
5
        {SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 200, SERVO_DIRECTION_BOX_DOWN},
6
        {SERVO_IDENTIFIER_ARM, 0, MAX_SERVO_ARM_RADIUS, 200, SERVO_DIRECTION_BOX_DOWN},
7
        {SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 600, SERVO_DIRECTION_BOX_UP},
8
        {SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 200, SERVO_DIRECTION_BOX_DOWN}
9
};
10
11
12
executeAction(&actionTwo);

Um später es mal abwechselnd mit actionOne und actionTwo zu verarbeiten. 
Ich würde gerne den Code kurz und nach dem DRY Prinzip aufbauen.

Vielen Dank für Hilfe
Grüße structeur

von g457 (Gast)


Lesenswert?

executeAction(actionOne, sizeof(actionOne) /sizeof(actionOne[0]));

HTH

von x^2 (Gast)


Lesenswert?

structeur schrieb:
> Ich bin mir dessen bewusst, dass ich bei der Übergabe der Variable
> irgendwie was falsch mache, weil ich irgendwas Grundlegendes
> offensichtlich nicht verstanden habe.


Du kannst die Anzahl der Elemente in einem Array so nicht bestimmen. 
Wenn du die Anzahl der Elemente in einem Array, das du als Parameter in 
eine Funktion gibst, wissen musst, musst du dies als zusätzlichen 
Parameter übergeben. Beispiel:
1
void f(TYPE* x, size_t count);

Den Trick den du anwenden möchtest der funktioniert nur in einem 
statischen Kontext, also wenn du es auf ein globales oder statisches 
Objekt anwendest. Folgendes geht:
1
static TYPE[] = { {}, {}, ... };
2
...
3
count = sizeof(TYPE)/sizeof(TYPE[0]);

Für die Parameter einer Funktion geht dies nicht. Der Grund ist einfach: 
Im obigen Fall gibt es ein einziges Objekt, dessen Größe der Compiler 
zur Compile-Zeit bereits kennt. Eine Funktion kann als Argument von 
extern aber jedes beliebige Objekt zugeführt bekommen. Ein anderer 
Denkansatz ist, dass die Auswertung von sizeof() immer zur Compile-Zeit 
statt findet. Also so als hättest du statt sizeof() einfach eine 
Konstante geschrieben. Der Compiler ist lediglich so nett, dir diese 
Konstante zur Compile-Zeit zu berechnen.

von x^2 (Gast)


Lesenswert?

Korrektur:
1
static TYPE x[] = { {}, {}, ... };
2
3
count = sizeof(x)/sizeof(x[0])

Späte Stunde...

von structeur (Gast)


Lesenswert?

Hallo g457,

erst ein mal Danke für die Antwort. Die half mir auf die Sprünge auch 
wenn ich sie anfangs nicht mit einer funktionierenden Lösung in Einklang 
bringen konnte.

Bei deiner Ausführung sind zwei Argumente von Nöten und das ist für 
erstmal eine Lösung.

Für die Nachwelt: Das Problem ist, dass die Ermittlung von countElements 
innerhalb der Methode executeAction falsch ist, die Übergabe aber von 
actionOne korrekt ist sowie die Definition der Methode.

Meine bisherige Lösung sieht so aus, dass die Methode einen zweiten 
Parameter erhält.

Falls jemand wüsste, wie es auch ohne den zweiten Parameter klappen 
könnte, wäre ich für ein Beispiel der richtigen Schreibweise dankbar.

Hier nun meine funktionierender Code:
1
const int SERVO_IDENTIFIER_BOX = 1;
2
const int SERVO_IDENTIFIER_ARM = 2;
3
4
const bool SERVO_DIRECTION_LOOP_POSITIVE = true;
5
const bool SERVO_DIRECTION_LOOP_NEGATIVE = false;
6
7
const bool SERVO_DIRECTION_BOX_UP = SERVO_DIRECTION_LOOP_POSITIVE;
8
const bool SERVO_DIRECTION_BOX_DOWN = SERVO_DIRECTION_LOOP_NEGATIVE;
9
const bool SERVO_DIRECTION_ARM_UP = SERVO_DIRECTION_LOOP_POSITIVE;
10
const bool SERVO_DIRECTION_ARM_DOWN = SERVO_DIRECTION_LOOP_NEGATIVE;
11
12
const int MAX_SERVO_ARM_RADIUS = 370;
13
const int MAX_SERVO_BOX_RADIUS = 100;
14
15
/**
16
 * 
17
 * debugString = Gibt den zu verarbeiteten Loop als String aus.
18
 * address = gibt den Servo an, der angesprochen werden soll
19
 * pos_begin = aus welchem Winkel der Servoarm begonnen werden soll 
20
 * pos_stop = bei welchem Winkel der Servoarm seinen Maximalpunkt erreicht haben soll
21
 * delay_loop = Pause in Millisekunden innerhalb einer Schleife (Start-Endpunkt)
22
 * move_direction = Gibt an, ob die Schleife auf- oder abwärts durchlaufen werden soll.
23
 */
24
25
typedef struct {
26
    char* debugString;
27
    int address;
28
    int pos_begin;
29
    int pos_stop;
30
    int delay_loop;
31
    bool move_direction ;
32
    int optional_repeat ;
33
    int optional_repeat_delay ;
34
} ServoLoop;
35
36
ServoLoop actionOne[] = {
37
        {"a", SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 200, SERVO_DIRECTION_BOX_UP},
38
        {"b", SERVO_IDENTIFIER_ARM, 0, MAX_SERVO_ARM_RADIUS, 200, SERVO_DIRECTION_ARM_UP},
39
        {"c", SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 200, SERVO_DIRECTION_BOX_DOWN},
40
        {"d", SERVO_IDENTIFIER_ARM, 0, MAX_SERVO_ARM_RADIUS, 200, SERVO_DIRECTION_BOX_DOWN}
41
};
42
43
ServoLoop actionTwo[] = {
44
        {"e", SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 200, SERVO_DIRECTION_BOX_UP},
45
        {"f", SERVO_IDENTIFIER_ARM, 0, 50, 200, SERVO_DIRECTION_ARM_UP},
46
        {"g", SERVO_IDENTIFIER_ARM, 0, MAX_SERVO_ARM_RADIUS, 200, SERVO_DIRECTION_ARM_UP},
47
        {"h", SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 200, SERVO_DIRECTION_BOX_DOWN},
48
        {"i", SERVO_IDENTIFIER_ARM, 0, MAX_SERVO_ARM_RADIUS, 200, SERVO_DIRECTION_BOX_DOWN},
49
        {"j", SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 600, SERVO_DIRECTION_BOX_UP},
50
        {"k", SERVO_IDENTIFIER_BOX, 0, MAX_SERVO_BOX_RADIUS, 200, SERVO_DIRECTION_BOX_DOWN}
51
};
52
53
void setup() {
54
  Serial.begin(9600);
55
}
56
57
void loop() {
58
//executeAction(actionOne, sizeof(actionOne) / sizeof(ServoLoop));
59
  executeAction(actionTwo, sizeof(actionTwo) / sizeof(ServoLoop));
60
 delay(1000);
61
}
62
63
void executeAction(ServoLoop action[], int countElements) {
64
  Serial.println("ACTION_BEGINN");
65
  Serial.println("Count of elements: " + (String) countElements);
66
  for (int i = 0; i < countElements; i++) {
67
    Serial.println(action[i].tes);
68
    delay(300);
69
  }
70
71
  Serial.println("ACTION_FINISHED");
72
}

Für weitere Optimierungen bin ich gerne Bereit und für jeden Hinweis 
dankbar.

Grüße
structeur

von structeur (Gast)


Lesenswert?

x^2 schrieb:
> structeur schrieb:
>> Ich bin mir dessen bewusst, dass ich bei der Übergabe der Variable
>> irgendwie was falsch mache, weil ich irgendwas Grundlegendes
>> offensichtlich nicht verstanden habe.
>
> Du kannst die Anzahl der Elemente in einem Array so nicht bestimmen.
> Wenn du die Anzahl der Elemente in einem Array, das du als Parameter in
> eine Funktion gibst, wissen musst, musst du dies als zusätzlichen
> Parameter übergeben. Beispiel:
> void f(TYPE* x, size_t count);
>
> Den Trick den du anwenden möchtest der funktioniert nur in einem
> statischen Kontext, also wenn du es auf ein globales oder statisches
> Objekt anwendest. Folgendes geht:
> static TYPE[] = { {}, {}, ... };
> ...
> count = sizeof(TYPE)/sizeof(TYPE[0]);
>
> Für die Parameter einer Funktion geht dies nicht. Der Grund ist einfach:
> Im obigen Fall gibt es ein einziges Objekt, dessen Größe der Compiler
> zur Compile-Zeit bereits kennt. Eine Funktion kann als Argument von
> extern aber jedes beliebige Objekt zugeführt bekommen. Ein anderer
> Denkansatz ist, dass die Auswertung von sizeof() immer zur Compile-Zeit
> statt findet. Also so als hättest du statt sizeof() einfach eine
> Konstante geschrieben. Der Compiler ist lediglich so nett, dir diese
> Konstante zur Compile-Zeit zu berechnen.

Danke für deine Ausführung. Die ist einleuchtend. Ist ja nicht PHP. :)

Verfahre ich dann damit "falsch"?
1
sizeof(actionOne) / sizeof(ServoLoop)

Ich habe jetzt
1
sizeof(actionOne) / sizeof(actionOne[0])

in Verwendung.

von M.K. B. (mkbit)


Lesenswert?

structeur schrieb:
> Falls jemand wüsste, wie es auch ohne den zweiten Parameter klappen
> könnte, wäre ich für ein Beispiel der richtigen Schreibweise dankbar.

1) struct { ServoLoop* pStart, int iLen }; als Übergabeparameter. Ist 
aber dann das gleiche, wie zwei Parameter, nur nochmal gekapselt.

2) Einen speziellen Wert für ServoLoop an dem man erkennt, dass dies der 
letzte ist. So wie bei C-String die '\0' als Ende. Du hast dann aber ein 
sizeof(ServoLoop) im Speicher das du nicht brauchst. Außerdem ist das 
bei Random Access auf das Array blöd, weil man erst suchen muss, wo das 
Ende ist. Mit der Längenangabe kann man es einfach prüfen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.