ich möchte ein Protokoll zur Kommunikation zwischen Arduino und Android erstellen. Dies möchte ich gern für zukünftige Projekte weitestgehend Modular aufbauen. Aus diesem Grund habe ich eine Variable Payload im struct PACKET. Hier sollen später die Daten gespeichert werden. Da ich allerdings nicht weiß, wieviele Daten ich transportieren möchte, soll dieser Array variabel (ohne feste Größe) deklariert werden. Die Ausgabe die ich bekomme stimmt allerdings nicht. Was mache ich falsch?:
Das liegt nicht am struct selbst. Der Teil geht bei mir in Visual C++. Auf dem Arduino habe ich es nicht ausprobiert.
Ich glaube was schief geht ist das:
packetSize = sizeof(packet);
Das sizeof() wird dir nur den 2-Byte Zeiger zählen. Nicht die Größe des Arrays. Bei mir kommt da in Visual C++ 16 raus. Was daran liegt das Zeiger auf dem PC natürlich 32 Bit haben. Auf dem Arduino sollte es 14 sein.
Dann hast du das Problem dass du da nur eine flache Kopie hast. Du willst wahrscheinlich eine tiefe Kopie. Also musst du Payload extra kopieren! Das steht durch malloc() in einem anderen Speicherbereich und wird durch memcpy() nicht kopiert. Die Kopie des structs passt so. Auch mit 14 Bytes. Aber du kopierst da halt nur den Zeiger. Nicht den Inhalt des Arrays!
Wenn du das in einen Puffer für eine Übertragung willst, musst du das Ziel-Array wahrscheinlich um -2 + 12 anpassen und dann payload in die letzten 12 Bytes kopieren.
Ich weiss zwar was du meinst, aber nicht, wie ich es umsetzen kann. Die 12 ist eigentlich nur ein Beispiel. Geplant sind 128-Bytes, die ich für den Payload reservieren möchte.
C / C++ ist für mich noch relatives Neuland. Ich bin eingefleischter PHP und Java-Programmierer und breche mir gerade mit der deklaration eines dynamischen Array einen ab. Hänge an diesem Problem jetzt seit gut 3 Tagen und komme einfach nicht weiter.
Ich nehme mal an hier geht es um ein Übertragungsprotokoll. Du willst einen Header und die Nutzdaten in einen Puffer schreiben um diesen auf einmal irgendwie zu versenden.
Du musst deinen Puffer so groß machen:
Größe des structs - 2 + Payload Größe
2, weil Zeiger hier eben 16 Bit breit sind. Du willst aber nicht den Zeiger übertragen, sondern die Daten auf die er zeigt. Also brauchst du auch Speicher für die eigentlichen Daten. Es sei denn du reservierst einfach mal x + 128 Bytes, musst du den Puffer dann auch mit malloc() anlegen, da die Größe nicht mehr unbedingt zur Compile-Zeit feststeht.
Dann kopierst du erst das struct in den Puffer. Und dann ab der Stelle an der der Zeiger steht den Inhalt des Arrays. Du musst also zweimal memcpy() machen. Einmal mit dem struct. Einmal mit der Nutzlast.
Das läuft wie gesagt auch unter dem Stichpunkt tiefe Kopie/flache Kopie. Das Problem hat man hauptsächlich wenn man Kopien von Objekten mit dem Zuweisungs-Operator erstellt (auch in Java oder C#, da dort alles außer primitiven Datentypen eine Referenz ist!). Die meisten Artikel beziehen sich dann allerdings auf Copy-Konstruktoren was hier nichts hilft.
Übertragen (Serialisieren) von Objekten ist nicht trivial.
Den Zeiger uint8_t * payload brauchst du nicht zu übertragen, wohl aber die Größe der payload ( und den Inhalt, natürlich ).
Dwer Zeiger muss dann beim Erzeugen eines PACKET neu erstellt werden.
In C macht man es alternativ gern auch mal so, dass die struct packet ihre eigene variable Größe enthält und die payload Daten selbst, statt eines Zeigers darauf:
struct PACKET {
uint8_t size; // Gesamtgröße eines PACKET incl. payload
uint8_t header;
uint8_t length; // mir unklar, ob das redundant ist
uint8_t function;
uint8_t multivar;
uint8_t source[4];
uint8_t destination[4];
uint8_t payload[1]; // variable Größe
};
size kannst du beim Empfangen nehmen um dir den erforderlichen Speicher zu besorgen,
und um zu wissen, wieviele bytes ( inkl. payload ) kopiert werden müssen.
@Serenifly: Gibt es evtl. ähnliche Beispiele? Wie würdest du soetwas realisieren? Bietet der Arduino überhaupt ausreichend RAM, wenn ich zur Laufzeit solche "Speichermengen" reservieren muss. Des Weiteren muss der Code relativ schnell verarbeitet werden, da ich z.B. Spannungsverläufe und Drehzahlen an mein Handy übertragen möchte.
@michael_x : Das wär natürlich eine Möglichkeit, aber ich würde das Struct gern "hart"-Coden und daraus eine Lib erstellen. Ich mache mir jetzt einmal die Arbeit, eine Kommunikationsschnittstelle zu programmieren, die ich dann in all meinen Projekten nutzen möchte.
Normalerweise wird bei solchen sehr kleinen Mikrocontrollern wie dem AVR mit sehr wenig RAM von dynamischen Speicher abgeraten. Das hat aber viel mit der Gefahr der Heap-Fragmentierung zu tun. Das trifft aber nicht immer zu. Wenn man z.B. dynamischen Speicher einmal anlegt und nicht mehr freigibt passiert nichts. Oder wenn man ihn wie bei dir anlegt und gleich danach wieder freigibt. Du hast auch nur maximal ein paar hundert Byte. Also spricht nicht unbedingt was dagegen.
Das kostet natürlich etwas Performance. Wie viel müsste man messen. Das kann ich so nicht sagen. Das gleiche gilt auch für die Abstraktion in einer Klasse. Ein völlig allgemeines Protokoll ist zwar sehr schön, aber auch nicht das effizienteste. Wenn du wirklich maximale Performance brauchst, schreibe es lieber auf genau deine Anwendung zugeschnitten.
Was aber auffällt, ist das der Kopiervorgang eigentlich überflüssig ist. Du kannst statt dessen einfach das struct auf einen byte* casten und wie ein Array behandeln. Dabei gilt aber das gleiche Problem mit der tiefen "Kopie" wie vorher! Du darfst nur bis zum Zeiger auf "payload" iterieren und musst dann über "payload" selbst getrennt iterieren, da dieses Array speicher-technisch nicht Teil des structs ist.
EDIT: genaugenommen musst du die Adresse des structs casten. Also &packet auf byte*:
Nochmal Vielen Dank für eure Hilfe, aber ich weiß einfach nicht, wie ich eure Tipps richtig umsetzen kann. Ich werde wohl wieder zurück gehen und einfach ganz normale Strings per Serial senden. Dynamische Arrays sind wohl noch zu komplex für mich.