Pages: [1]   Go Down
Author Topic: Google Protocoll Buffers zur Kommunikation zwischen Flash-Anwendung und Arduino  (Read 362 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 4
Posts: 50
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo,

ich möchte die Kommunikation zwischen einer Flash-Anwendung und einem Arduni Mega board etwas vereinfachen.
Bisher habe ich hierfür den CommandMessenger verwendet. Dieser kommt aber leider durcheinander wenn zu viele Nachrichten auf zu schnell hintereinander gesendet werden.

Ich habe jetzt von einem Freund erfahren, dass google Protocol Buffers ein sehr schlankes Kommunikationsprotokokl sein soll.
Hab mich da jetzt ein wenig eingelesen. Auf Seiten von Flash läuft alles schon. Ich habe die proto file erstellt, diese kompiliert und die Klasse erhalten. Nach Instanzierung kann ich die Daten beeinflussen, serialisieren und über den serial proxy von Tinker ans board schicken. So weit so gut.

Nun möchte ich auf Seiten von Arduino das ganze wieder auslesen.
Wie gehe ich hierbei vor?

Die c und h files für die messages habe ich bereits erstellt.

Wenn ich das richtig verstehe, müsste ich einen Puffer anlegen und im loop alle Daten mit Serial.read() in den Puffer schreiben. Richtig? Wie geht es dann weiter? Wie decode ich die Daten, sodass ich die Werte auslesen kann? Woher weiß ich dann welche Message das ist?

Ich hoffe ihr könnt mir helfen.
Die Beispiele, die ich finden konnte arbeiten irgendwie nicht mit Serial und sind auch keine .ino files. (Haben also auch keine loop)

Viele Grüße, Jörg


* project.JPG (25.98 KB, 320x305 - viewed 18 times.)
« Last Edit: June 04, 2014, 09:29:15 am by iamable » Logged

Switzerland
Offline Offline
Faraday Member
**
Karma: 111
Posts: 5242
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Google's Protocol Buffers ist nicht ein Kommunikationsprotokol, sondern ein Serialisierungsformat.

Welchen CommandMessenger hast Du verwendet? Link dazu wäre nicht übertrieben.

Welche Art von Nachricht möchtest Du übertragen?

Was meinst Du mit "Flash Anwendung"? Willst Du eine Adobe Air-Anwendung mit dem Arduino kommunizieren lassen?

Quote
Wenn ich das richtig verstehe, müsste ich einen Puffer anlegen und im loop alle Daten mit Serial.read() in den Puffer schreiben.

Mit Puffern solltest Du auf dem Arduino vorsichtig sein. Du bist Dir bewusst, dass Du von einem Gerät sprichst, das gerade mal 8kB (Mega2560) Speicher (RAM) hat? Wenn Du das serialisierte Paket in einen Puffer liest, dort analysierst und wieder in einer Struktur ablegst, bist Du schnell soweit, dass Du den Speicher komplett belegst. Dann wirst Du plötzlich ein eigenartiges Verhalten vom Arduino bekommen und nicht viel Freude am Resultat haben.

Ein Mikrokontroller wie der Arduino muss anders programmiert werden als ein PC. Er hat viel weniger RAM zur Verfügung, keine MMU (Memory Management Unit) und somit eine nicht sehr gemütliches Speicherbelegungsverhalten.

Erkläre uns, was Du erreichen willst und nicht, wie Du es erreichen willst. Wir können Dir dann vielleicht einen besseren Rat geben, was Du verwenden sollst. Google Protocol Buffers ist für die Kommnikation Browser<->Server gedacht (in erster Linie) und nicht für die Kommunikation mit einem Mikrokontroller.
Logged

Offline Offline
Jr. Member
**
Karma: 4
Posts: 50
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Welchen CommandMessenger hast Du verwendet?
http://playground.arduino.cc/Code/CmdMessenger

Quote
Was meinst Du mit "Flash Anwendung"? Willst Du eine Adobe Air-Anwendung mit dem Arduino kommunizieren lassen?
Ja genau.

Quote
Erkläre uns, was Du erreichen willst und nicht, wie Du es erreichen willst.
Letzt endlich möchte ich gesteuert von Flash aus verschiedene Aktionen auf dem Arduino ausführen, das wiederum verschiedene LED-Bänder steuert.

Flash hat verschiedene Nachrichten mit unterschiedlich vielen Parametern, die ans Arduino übermittelt werden sollen.
Der Command Messenger war mir sehr attraktiv, da ich nicht hundferte if else Verzeweigungen schrieben brauche und er das Mapping von der Command ID auf eine Methode gemacht hat. Sehr komfortabel in der Bedienung. Aber eben mit angesprochenen Problemen.
Logged

Offline Offline
Full Member
***
Karma: 2
Posts: 106
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Warum willst du irgendein festes Kommunikationsprotokoll benutzen?

Normalerweise reicht es doch wenn du dem Arduino nur Steuersignale sendest wie

1= LED an
2  = LED aus
r = Farbe rot
b = Farbe blau

Dies packst du in dir bekannte Steuerzeichen und gut is.

Oder benötigst du auch einen Rückkanal zum PC für Inputs vom Arduino?
Logged


Offline Offline
Jr. Member
**
Karma: 4
Posts: 50
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Was auf den LEDs abläuft ist deutlich komplexer als nur an aus oder rot und blau.
Es werden aufgrund von Parametern verschiedene komplexe Objekte generiert, die auf verschiedenen LED-Bändern angezeigt werden.

Nur als Beispiel: Es soll ein Streifen, der 10 Pixel lang ist, einen Verlauf von rot nach grün hat auf obersten Layer vom 40. zum 100. Pixel laufen mit einer Geschwindigkeit von 3 Pixeln pro Frame.

Hierfür muss eine Methode aufgerufen werden, die verscheidene Parameter braucht.
Logged

Offline Offline
Jr. Member
**
Karma: 4
Posts: 50
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hat sich wohl schon erledigt.
die Flash library scheint schon ne macke zu haben.

nichtmal das funktioniert:

Code:
var ambient:hello = new hello();
var byteArray:ByteArray = new ByteArray();

ambient.data = "hallo";
ambient.writeTo(byteArray);

var ambient2:hello = new hello();
ambient2.mergeFrom(byteArray);
trace(ambient2.data);
Logged

Offline Offline
Jr. Member
**
Karma: 4
Posts: 50
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

kann mir jemand helfen wie ich eine sichere und stabile kommunikation realisieren kann?
Logged

Switzerland
Offline Offline
Faraday Member
**
Karma: 111
Posts: 5242
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
kann mir jemand helfen wie ich eine sichere und stabile kommunikation realisieren kann?

Welche Information muss übertragen werden?

Wenn es um 10 RGB LEDs geht, dann kannst Du ja einfach 60 Zeichen übertragen (Hex-Form der RGB-Daten) mit Start- und Stop-Zeichen. Damit kriegst Du fast 200 Hz hin, was weit mehr ist als das menschliche Auge wahrnehmen kann.
Logged

Offline Offline
Jr. Member
**
Karma: 4
Posts: 50
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wie schon beschrieben gibt es verschiedene Nachrichten in verschiedenen Längen.
Der erste integer Wert soll immer eine Methode aufrufen. Dann folgt eine bestimmte Anzahl von Parametern, je nachdem welche ID der erste Wert hat.

z.b. könnten Nachrichten so aussehen

1,3,90,140,4;
2,30,120;
3;
4,9;
5,7,10,30,120,9;

Die Werte werden in ein ByteArray geschrieben und dann gesendet.
Die Frage ist jetzt wie ich das am Besten auf Seiten von Arduino interpretieren kann.
Mit dem CmdMessenger hat es eigentlich ganz gut geklappt, aber wenn es zu viele Nachrichten auf einmal waren die geschickt wurden, ist das Board anscheinend nicht mehr hinterher gekommen

Mir fehlt da auch etwas Hintergrundwissen:

Wenn ich zum beisiel im loop sowas mache:
Code:
if(Serial.available())
{
int comand = Serial.read();
}

und dann in einer switch case abfrage welche ID das ist, dann jeweils eine Methode aufrufe und in dieser dann mehrmals hintereinander Serial.read() aufgerufen wird, klappt das dann? Kann ich sicher stellen, dass dann wirklich schon alle Zeichen rein gekommen sind? eigentlich nicht oder? Muss ich solange in einen buffer einlesen bis das semikolon am ende gekommen ist und dann am ende den buffer auseinander pflücken? Wenn ja dann ist genau das doch das was der CmdMessenger tut oder? Ich weiß einfach nicht was ich besser machen soll als der CmdMessenger  smiley-roll-sweat smiley-cry smiley-sad
« Last Edit: June 05, 2014, 09:05:01 am by iamable » Logged

Offline Offline
Faraday Member
**
Karma: 128
Posts: 4128
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sowas kann man auch relativ schnell per Hand machen ohne was fertiges zu verwenden. Du kannst als erstes Byte die Länge der Nachricht senden. Dadurch weiß man wie viele Bytes man einlesen und interpretieren muss.

Alternativ kann man auch ASCII statt Binär senden. Das ist vielleicht etwas robuster, aber auch mehr Daten zu übertragen und man muss es dann wieder parsen. Gerade wenn du dich damit nicht auskennst, ist das die kompliziertere Variante.

Die aufzurufenden Funktionen kommen in ein Array aus Funktionszeigern (function pointer):
http://www.elektronik-bastelkeller.de/C_Ardu_8_7.php
Dann schickt man den Index im Array und kann die Funktion so direkt aufrufen

switch/case geht auch, ist aber mehr Code und schlechter erweiterbar.
« Last Edit: June 05, 2014, 09:10:22 am by Serenifly » Logged

Offline Offline
Jr. Member
**
Karma: 4
Posts: 50
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

OK das mit den Funktionszeiger ist ja schonmal cool. Danke dafür.

Den Teil davor habe ich noch nicht verstanden:

ok das erste Byte ist die Länge. So weit so gut.
Und dann? In eine Puffer schrieben? Oder wie komme ich dann wirklich an die Werte?
Kann ich wenn die Funktion aufgerufen wird wirklich sicher sein, dass alle Werte schon im Serial bereit stehen?
Nicht, dass ich ein Serial.read() aufrufe und der Wert noch gar nicht übermittelt wurde.

Manche arbeiten ja mit if(Serial.available() == size) oder sowas aber ich habe gelesen, dass es nicht gut ist das zu machen, da durchaus auch Bytes verloren gehen könnten und dann alles durcheinader kommt.
« Last Edit: June 05, 2014, 09:16:07 am by iamable » Logged

Offline Offline
Faraday Member
**
Karma: 128
Posts: 4128
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Generell ist seriell recht langsam. Selbst bei 115200 Baud brauchst du 1 / 115200 * 10 s pro Zeichen. Also ca. 86µs. Es dauert etwas bis alles da ist. Man muss nicht auf einmal warten bis alles empfangen ist, aber selbst man immer wenn was da ist gleich einliest, wartet man dazwischen auf Daten.


In einen Puffer zu schreiben ist wahrscheinlich das sinnvollste.

Dabei hast du zwei Optionen:
1.) ein statisches Array verwenden. Das geht einfach wenn du eine maximale Anzahl an Daten hast die übertragen werden, was hier wohl der Fall ist.
2.) dynamischen Speicher verwenden und den Puffer immer nur so groß machen wie er sein muss. Bringt hier eigentlich nichts, da du trotzdem genug Speicher brauchst um die maximale Größe zu empfangen wenn das auftritt.
Außerdem generell komplizierter und langsamer. Letzteres ist auch ein Grund weshalb solche allgemeinen Kommunikationsproktolle nicht am effizientesten sind und man u.U. mit etwas anwendungsspezifischem besser beraten ist.

Quote
Manche arbeiten ja mit if(Serial.available() == size) oder sowas aber ich habe gelesen, dass es nicht gut ist das zu machen, da durchaus auch Bytes verloren gehen könnten und dann alles durcheinader kommt.
Das hat den Nachteil, dass der serielle Empfangspuffer nur 63 Bytes groß ist. Deshalb sollte man den Puffer so schnell wie möglich leeren wenn man viele Daten erwartet. Also einfach auf Serial.available() > 0 abfragen und dann solange einlesen bis alles da ist. Das Ende weiß man bei Binär-Daten über die vorher übertragene Länge oder wenn man mit ASCII arbeitet mit einem Endzeichen (z.B. LF oder CR).


Du musst dich halt entscheiden was du machst:
1.) Daten binär als Rohe Zahlen übertragen. Das heißt "100" ist ein Byte mit dem Wert 100
2.) Daten als ASCII übertragen. Das heißt "100" sind drei Bytes. Dazu braucht man noch Trennzeichen zwischen den Zahlen.

Sowie du es jetzt es scheinst du einen String zu nehmen und diesen in eine Byte-Array zu wandeln. Das geht in Richtung Variante 2

Möglichkeiten gibt es da einige. Man kann z.B. auch die Daten als ASCII senden und statt erst mal den ganzen Text zu empfangen und dann zu wandeln, liest man immer genug Zeichen für eine Zahl ein und wandelt diese gleich und speichert dann nur die gewandelten Zahlen im Puffer. Das senkt den Speicherverbrauch.
« Last Edit: June 05, 2014, 09:40:04 am by Serenifly » Logged

Offline Offline
Jr. Member
**
Karma: 4
Posts: 50
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wenn ich die einzelnen Zahlenwerte in ein ByteArray schreibe und dann schicke ist es doch bestimmt besser, als wenn ich mir einen String mit Trennzeichen zusammen bastel und dann schicke oder? So hab ich ja schonmal nicht den overhead der Trennzeichen.
Die Frage ist, wie ich an den Anfang die Länge bekomme. Nicht jeder Zahlenwert braucht ja ein Byte oder? Oder kann ich einfach sagen, dass wenn ich 5 Zahlen (nicht größer als 255) in ein ByteArray schreibe, dass dann auch die Anzahl der Bytes gleich 5 ist? Wird auch für eine integer 1 ein Byte verwendet? Oh oh oh hab ganz schön Nachholbedarf was Datentypen und deren Speicheralokierung angeht ^^

Ich hoffe ihr bringt mir etwas Licht ins dunkel.

Ok angenommen ich habe dann das ByteArray abgeschickt mit der dann hoffentlich vorangestellten Länge.
Dann erzeuge ich einen statischen Puffer:
Code:
uint8_t in_buffer[10];
Dann schreibe ich mit der Größe auf folgende Weise in den Puffer:
Code:
Serial.readBytes((uint8_t*)in_buffer, packet_size);
führt er dann readBytes erst aus wenn auch wirklich die durch die Länge packet_size angegebene Länge an Paketen angekommen ist?

Hast du nicht vielleicht ein kleines Codebeispiel für mich?
Logged

Offline Offline
Faraday Member
**
Karma: 128
Posts: 4128
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

der kann ich einfach sagen, dass wenn ich 5 Zahlen (nicht größer als 255) in ein ByteArray schreibe, dass dann auch die Anzahl der Bytes gleich 5 ist?
Wie das in Flash genau aussieht, weiß ich nicht. Noch nichts damit gemacht. Solche Protokolle habe ich PC-seitig nur in Java und C# geschrieben.

Du kannst ja vorher mal die Länge des Arrays überprüfen. Aber wenn du ints in ein Byte Array schreibst, ist es durchaus wahrscheinlich, dass du dann 4 Bytes pro int hast.

Quote
Wird auch für eine integer 1 ein Byte verwendet? Oh oh oh hab ganz schön Nachholbedarf was Datentypen und deren Speicheralokierung angeht
Auf dem Arduino hat ein int 2 Bytes, da es ein 8 Bit System ist. Auf dem PC 4. Da muss man etwas aufpassen.

Flash scheint leider sehr limitiert zu sein was die Datentypen betrifft:
http://help.adobe.com/en_US/as3/learn/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9c.html

Byte/char oder short (2 Byte Integer) scheint es da nicht zu geben :o

Obwohl man je nach dem was man macht trotzdem Bytes schicken kann. z.B. hier:
http://stackoverflow.com/questions/7706452/byte-in-actionscript-3-from-c-to-as3
Da hat die Socket Klasse eine Methode die einen int nimmt und daraus beim Senden ein Byte macht.

Schau mal ob es sowas auch für deine Schnittstelle gibt (USB nehme ich mal an). Gibt es da vielleicht sowas wie serial.writeByte()?


Quote
Dann schreibe ich mit der Größe auf folgende Weise in den Puffer:
Code:
Serial.readBytes((uint8_t*)in_buffer, packet_size);
Die Methode blockiert bis die Bytes da sind oder die Timeout Zeit erreicht ist. Das sollte dir klar sein. Das kann ok sein, aber es kann auch Probleme verursachen. Hängt von der Anwendung ab.

Und auch hier wieder: der Eingangspuffer ist nur 63 Bytes groß! Wenn du mehr Daten hast, geht das nicht



Die andere Möglichkeit ist Byte für Byte einzulesen und per Hand mitzuzählen. Hier ist mal die ASCII Variante davon:
Code:
const int SERIAL_BUFFER_SIZE = 60;
char serialBuffer[SERIAL_BUFFER_SIZE];

void loop()
{
    if(readSerial() == true)
   {
   }
}

bool readSerial()
{
static byte index;

if(Serial.available() > 0)
{
char c = Serial.read();

if(c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
  {
serialBuffer[index++] = c;
}
else if(c == '\n')
{
serialBuffer[index] = '\0';
index = 0;
return true;
}
}
return false;
}

Das liest einen C String ein (nicht Zahlen!). readSerial() wird solange aufgerufen bis ein Linefeed ankommt. Wenn nichts im Eingangspuffer steht kann man solange andere Dinge abarbeiten. Wenn dann alles da ist liefert die Methode "true" zurück und man kann die Daten verarbeiten

Das kann man auch leicht anpassen indem man abbricht wenn die Zählvariable gleich der zu erwarteten Bytes ist.
Logged

Pages: [1]   Go Up
Jump to: