read byte array

Hallo,

hab da nen Problem.
Und zwar nutze ich die ArduinoConnector um mit einer Flash Applikation zu kommunizieren.
Hierfür füge ich einem ByteArray 6 integer hinzu und schicke sie über den Socket zum Arduino

override public function send():void
		{
			var byteArray:ByteArray = new ByteArray();
			
			for (var i:int = 0; i < message.length; i++)
			{
				byteArray.writeByte(message[i]);
			}
			
			socket.writeBytes(byteArray);
			
			timer.start();
		}

Nun schaffe ich es nicht diese Werte auf Seiten von Arduino vernünftig auszulesen und je nach Wert die richtige Methode auszuführen.

Hier mein bisheriger code:

void loop() 
{  
  //cmdMessenger.feedinSerialData();

  checkForSerialData();
  
  layerManager.Trigger();

  delay(10);
}

void checkForSerialData()
{
	while(Serial.available() > 0)
	{
		if(index < max_message_length)
		{
			inByte = Serial.read(); // Read a Byte
			Serial.println(inByte);
			inData[index] = inByte; // Store it
			index++;
			if (index == max_message_length)
			{
				checkID(inData[0]);
				index = 0;
			}
		}
	}
}

in der methode checkID() wird dann geprüft ob die erste empfangene Zahl einer ID entspricht, die wiederum zugehörig zu einer Methode ist. Wenn die IDs übereinstimmen wird diese Methode ausgeführt und verwendet dann die restlichen Zahlen aus inData[].

Leider komme ich nie soweit, da die IDs niemals matchen. Wenn ich inByte ausgeben lasse werden ganz andere Zahlen angezeigt als ich schicke.
Was mache ich hier falsch?

btw inByte und inData sind vom Typ int

Ich hoffe mir kann jemand helfen. Weiß nicht mehr weiter.
Vielen Dank schonmal

Ist aber für deinen Anwendungsfall vielleicht etwas zu allgemein und kompliziert. Wenn du weißt dass du immer 6 Bytes empfängst könntest auch auf available() >= 6 abfragen und dann alles in einer for-Schleife auf einmal einlesen.

inByte und inData sind vom Typ int

Ich nehmen an dir ist klar dass read() nur ein Byte auslesen kann? Also Werte von 0-255. Das Array int zu machen ist also unsinnig. Schadet aber nichts außer dass es Speicher verschwendet.

Hast du auch auf PC Seite ein dein message Array als unsigned char/byte deklariert? Wenn du da int hast, dann hast du 4 Bytes pro Integer! Wenn du ein 6 Byte int Array in Byte konvertierst hast du dann ein 24 Byte Array. Das produziert natürlich Unsinn.

Alternativ geht vielleicht sowas in der Art:

for (var i:int = 0; i < message.length; i++)
{
    socket.writeByte(message[i]);
}

Keine Ahnung ob es eine writeByte() Methode gibt, aber vielleicht kann man einzelne Byte schicken. Selbst wenn message dann ein int Array ist, sollte jeder Wert dann automatisch auf Byte gecastet werden.

Ich nehme an dein Fehler liegt eher auf der Sende-Seite. Lass dir da mal ausgeben was du eigentlich schickst.

Wenn du übrigens Funktionen anhand einer Zahl aufrufen willst, schau dir mal Function Pointer und dann Arrays aus Function Pointern an. In C/C++ muss man das nicht über switch/case machen.

Serenifly:
Alternativ geht vielleicht sowas in der Art:

for (var i:int = 0; i < message.length; i++)

{
    socket.writeByte(message[i]);
}

das war schonmal gut. so kommt jetzt erstmal kein mist mehr an.
AAAber es gibt dann da noch ein weiteres Problem:
Und zwar muss ich auch werte schicken die größer sind als 256.
Wie gehe ich dann damit um?

Ok, dann wird es wieder komplizierter und das oben geht schon wieder nicht.

Also ich kenne mich mit Flash nicht aus. Wenn ich das korrekt lese sind die Datentypen extrem stark eingeschränkt. Es gibt lediglich int und uint (32 Bit) und kein short (16 Bit). Das ist schon mal Scheiße. Wenn du damit ein int Array hast dann hat jeder Wert 4 Bytes.

Ich würde das dann per Hand in zwei Bytes trennen und nicht über ByteArray gehen:

for (var i:int = 0; i < message.length; i++)
{
    socket.writeByte(message[i] >> 8);          //high Byte senden
    socket.writeByte(message[i]);              //low Byte senden
}

Auf Empfangsseite musst du dann immer zwei Bytes auslesen. Dann muss man das Kommando Byte am Anfang getrennt behandeln. Geht vielleicht so

void checkForSerialData()
{
	static boolean commandByteReceived;
        
        if(Serial.available() > 0)
	{
	         if(commandByteReceived == false)
                 {
                     commandByteReceived = true;
                     inData[index++] = Serial.read();
                 }
                else if(Serial.available() >= 2 && index < max_message_length)
		{
			byte high = Serial.read();
                        byte low = Serial.read();
                        inData[index++] = word(high, low);
                        Serial.println(word(high, low));

			if (index == max_message_length)
			{
				checkID(inData[0]);
				index = 0;
                                commandByteReceived = false;
			}
		}
	}
}

Warnung: Das ist frei geraten und geschrieben. Nicht getestet! Keine Garantie dass das so geht

Man könnte vielleicht auch mit Modulo-Division oder einem & 1 arbeiten, so dass man bei jedem zweiten read() das eingelesene Byte anders behandelt.

Ein Problem dass so sofort auffällt ist das das Programm aus dem Tritt kommt, wenn längere Nachrichten als erwartet gesendet werden. Aber das ist bei Byte-basierender Kommunikation (im Gegensatz zu Strings) generell ein Problem.

Also ich kenne mich mit Flash nicht aus. Wenn ich das korrekt lese sind die Datentypen extrem stark eingeschränkt. Es gibt lediglich int und uint (32 Bit) und kein short (16 Bit). Das ist schon mal Scheiße. Wenn du damit ein int Array hast dann hat jeder Wert 4 Bytes.

Ansich hast du erstmal recht aber ByteArray gibt die Möglichkeit, ein short int zu schreiben. Dort werden dann lediglich die ersten 16 Bit geschrieben und der Rest ignoriert. Wenn ich also drauf achte, dass mein Wert nicht größer wird sollte das ja klappen.
http://help.adobe.com/de_DE/FlashPlatform/reference/actionscript/3/flash/utils/ByteArray.html#writeShort()

macht es eventuell sinn ans ende des ByteArrays ein Zeichen zur Markierung des Endes zu setzen und dann mit readBytesUntil zu lesen? Denn meine Nachrichten sind unterschieldich lang. manchmal wird nur ein command gesendet und manchmal noch bis zu 5 Parameter (je nach commandID). Bisher habe ich die Bytes immer noch mit 255 aufgefüllt um die richtige Länge zu erreichen.

Serenifly:
Auf Empfangsseite musst du dann immer zwei Bytes auslesen. Dann muss man das Kommando Byte am Anfang getrennt behandeln. Geht vielleicht so

void checkForSerialData()

{
static boolean commandByteReceived;
       
       if(Serial.available() > 0)
{
        if(commandByteReceived == false)
                {
                    commandByteReceived = true;
                    inData[index++] = Serial.read();
                }
               else if(Serial.available() >= 2 && index < max_message_length)
{
byte high = Serial.read();
                       byte low = Serial.read();
                       inData[index++] = word(high, low);
                       Serial.println(word(high, low));

		if (index == max_message_length)
		{
			checkID(inData[0]);
			index = 0;

commandByteReceived = false;
}
}
}
}

ahh cool das mit dem word kann ich gut gebrauchen. hab mich nämlich schon gefragt wie ich die einzelnen bytes dann wieder zu nem integer zusammen fügen kann.

Vielen Dank für deine Hilfe.

iamable:
Ansich hast du erstmal recht aber ByteArray gibt die Möglichkeit, ein short int zu schreiben. Dort werden dann lediglich die ersten 16 Bit geschrieben und der Rest ignoriert. Wenn ich also drauf achte, dass mein Wert nicht größer wird sollte das ja klappen.
ByteArray - Adobe ActionScript® 3 (AS3 ) API-Referenz

Ok, dann passt das :slight_smile:

Eventuell musst du noch auf die Endianess achten, also die Byte Ordnung, aber das kann man besser auf Arduino Seite anpassen. Wenn das nicht passt merkst du das aber sofort wenn du dir die empfangenen Bytes ansiehst.

macht es eventuell sinn ans ende des ByteArrays ein Zeichen zur Markierung des Endes zu setzen und dann mit readBytesUntil zu lesen?

Das ist nur sinnvoll wenn man die Daten als String sendet. Also ASCII codiert. Es sei denn du kannst wirklich sicherstellen, dass der Endwert nie vorher vorkommt. Was bei Binär-Werten sehr schwer ist.

Und wenn man Strings bis zu einem Endzeichen einliest (i.d.R. dann CR oder LF) macht man das besser so:
http://forum.arduino.cc/index.php?topic=252337.msg1786913#msg1786913
Wie du siehst, praktisch genauso wie du es schon hier machst

Das wäre auch eine Möglichkeit, aber das Datenvolumen steigt da rapide da man pro Ziffer und Zeichen 1 Byte hat. Dabei trennt man die Zahlen durch ein Komma ab- Den String kann man dann dem Empfang splitten und konvertieren (strtok() + atoi()).

Oder was auch gut geht und Speicher spart ist die Ziffern schon beim Empfang aufzuaddieren:
http://forum.arduino.cc/index.php?topic=257248.msg1819200#msg1819200

Der Code liest nur eine Zahl ein. Aber man kann da auch auf ein Trennzeichen abfragen, eine Zahl abspeichern (in einem Array) und dann weiter einlesen.

Denn meine Nachrichten sind unterschieldich lang. manchmal wird nur ein command gesendet und manchmal noch bis zu 5 Parameter (je nach commandID). Bisher habe ich die Bytes immer noch mit 255 aufgefüllt um die richtige Länge zu erreichen.

Ok, das ist prinzipiell kein Problem. Du musst halt nach dem Empfang des Kommando-Bytes "max_message_length", d.h. die Abbruch-Bedingung auf den entsprechenden Wert setzten.

Auf dem Arduino habe ich das in der Art noch nicht gemacht, aber ich habe das mal auf einem ARM Cortex mit einer Java VM und einem PC mit C# am Ende andere genauso gemacht. Da konnte man Tausende 32 Bit Integer hin und her schicken. Ohne Fehler.

Mach aber erst mal Pakete fester Länge. Wenn das dann geht kannst du sie variabel machen.

ahh cool das mit dem word kann ich gut gebrauchen. hab mich nämlich schon gefragt wie ich die einzelnen bytes dann wieder zu nem integer zusammen fügen kann.

Alternativ Bit-Schieben und Verodern. Das geht immer. word() macht auch nur sowas:

inline unsigned in word(byte high, byte low)
{
     return ((unsigned int)high << 8) | low;
}

ich hab wirklich das gefühl, dass wir uns der lösung nähern.

ich versuche deshalb alles nochmal auf teilprobleme runter zu brechen.
zu aller erst möchte ich eigentlich gerne das bytearray verwenden.
sorry dass ich dich mit flash code belästige aber der sieht jetzt mal so aus:

var byteArray:ByteArray = new ByteArray();
			byteArray.writeByte(5);
			trace("byte array length: " + byteArray.length);
			arduinoSocket.writeBytes(byteArray);

es wird wirklich nur ein byte gesendet laut length.

auf seiten von arduino möchte ich das byte auslesen und einfach nur wieder ausgeben:

if(Serial.available() > 0)
	{
		byte inByte3;
		inByte3 = Serial.read();
		Serial.println(inByte3);
	}

leider bekomme ich niemals den wert zurück, den ich sende.
Komisch ist auch, dass ich beim starten des Serial monitors immer erstmal meherer 255 ausgegeben bekomme...wo können die denn her kommen und können die mir das ganze verfälschen?

print() schickt ASCII

write() schickt Binär-Daten

puuuh oh man das problem habe ich gelöst.
Und zwar ist die Proxy-Klasse zum Verbinden mit Arduino fehlerhaft.
Zumindest schreibt sie die Bytearrays falsch:

https://code.google.com/p/as3-arduino-connector/issues/detail?id=27

Ok dann mach ich jetzt mal weiter mit der auswertung

Kriege massig fehlermeldung zu folgendem code:

byte inData[10];
byte index = 0;
byte messageLength = 0;

void setup()
{
	Serial.begin(9600);
}

void loop()
{
	static boolean commandByteReceived;

	if(Serial.available() > 0)
	{
		if(commandByteReceived == false)
        {
			commandByteReceived = true;
            inData[index] = Serial.read();
			calculateMessageLength(inData[index]);
			index++;
        }
		else if(Serial.available() >= 2 && index < messageLength)
		{
			byte high = Serial.read();
            byte low = Serial.read();
            inData[index] = word(high, low);
            Serial.println(word(high, low));
			index++;

			if (index == messageLength)
			{
				checkID(inData[0]);
				index = 0;
                commandByteReceived = false;
			}
		}
	}
}

void calculateMessageLength(int commandID)
{
	switch(commandID)
	{
	case 5:
		{
			messageLength = 3;
			break;
		}
	}
}

void checkID(int commandID)
{
	Serial.println("checkID");


	//TODO: function pointer
}

die meldungen lauten wie folgt:

ArduinoProtocol.ino:23: error: ISO C++ forbids comparison between pointer and integer
ArduinoProtocol.ino:27: error: invalid types 'byte [10][char*(const char*, int)]' for array subscript
ArduinoProtocol.ino:29: error: ISO C++ forbids incrementing a pointer of type 'char* ()(const char, int)'
ArduinoProtocol.ino:29: error: lvalue required as increment operand
ArduinoProtocol.ino:31: error: ISO C++ forbids comparison between pointer and integer
ArduinoProtocol.ino:34: error: assignment of function 'char* index(const char*, int)'
ArduinoProtocol.ino:34: error: cannot convert 'int' to 'char*(const char*, int)' in assignment
Error compiling

Kannst du damit was anfangen?
Schon die erste wundert mich...habe doch gar keinen pointer oder?

Du hast schon einen Zeiger. In C sind Array-Variablen Zeiger auf das erste Element. Das ist kein komplexer Datentyp wie in anderen Sprachen. inData ist das gleiche wie &inData[0]

Das sind allerdings sehr seltsame Fehlermeldungen. Ich sehe auch ich nicht wo er sich da aufhängt. Ich habe das mal bei mir rein kopiert (1.5.7. BETA) und da kompiliert es :~

habs endlich hinbekommen...zum letzten Problem mit den Fehlermeldungen:
Irgendwie sind mir abhängigkeiten zu anderen projekten rein gerutscht, die hier die fehlermeldungen verursacht haben.
das hat sich erledigt.

hier nun mein fertiges und super laufendes script:

void checkForSerialData()
{
	if(Serial.available() > 0)
	{
		if(commandByteReceived == false)
        {
			commandID = Serial.read();
			
			if (commandIDIsValid())
			{
				commandByteReceived = true;
				inData[myIndex] = commandID;
				myIndex++;

				while(myIndex < messageLength)
				{
					if(Serial.available() > 0)
					{
						inData[myIndex] = Serial.read();
						myIndex++;
					}
				}

				runCommand();
				myIndex = 0;
                commandByteReceived = false;
			}
			else
			{
				commandByteReceived = false;
				myIndex = 0;
				Serial.flush();
			}
			
        }
	}
}

boolean commandIDIsValid()
{
	boolean validity = false;

	switch(commandID)
	{
		case 0:
		{
			//setComet
			validity = true;
			messageLength = 8;
			break;
		}
		case 1:
		{
			//stopComet
			validity = true;
			messageLength = 1;
			break;
		}
		case 2:
		{
			//changeEndComet
			validity = true;
			messageLength = 3;
			break;
		}
		case 3:
		{
			//setSpot
			validity = true;
			messageLength = 6;
			break;
		}
		case 4:
		{
			//showKA
			validity = true;
			messageLength = 1;
			break;
		}
		case 5:
		{
			//hideKA
			validity = true;
			messageLength = 1;
			break;
		}
		case 6:
		{
			//startup
			validity = true;
			messageLength = 1;
			break;
		}
		default:
		{
			Serial.println("wrong commandID");
			validity = false;
		}
	}

	return validity;
}



void runCommand()
{
	Serial.print("run command");
	Serial.println(commandID);

	(*func_ptr[commandID])();
}

in den command methoden wird dann nur noch inData ausgewertet und die zugehörige aktion ausgeführt:

void setComet()
{
	//Serial.println("setComet");

	cometVisible = true;

	cometStartPx = word(inData[1], inData[2]);
	cometEndPx = word(inData[3], inData[4]);
	cometSpeed = inData[5];
	cometDirection = inData[6];
	cometLength = inData[7];
  
	comet_red.SetComet(cometVisible, cometStartPx, cometEndPx, cometSpeed, cometDirection, cometLength);  
}

VIiiiiieeeelen vielen dank für die Hilfe.
Finds richtig gut, dass hier auch solchen Anfängern wie mir geholfen wird :wink:

:slight_smile:

Bei dem Function Pointer Array solltest du sicherheitshalber noch abfragen ob der Index vorhanden ist. Man kann natürlich Flash-seitig aufpassen dass das passt, aber wenn mal was schief geht hat das katastrophale Auswirkungen, da er dann willkürlich in irgendeine Speicher Adresse springt.

typedef void (*funcPtr)();
funcPtr functions[] = { func1, func2, func3 };
const int functionCount = sizeof(functions) / sizeof(funcPtr);

functionCount gibt dir dann die Anzahl der definierten Funktionen im Array an. Du kannst dann überprüfen ob commandID in dem Array enthalten ist

Du hast die Funktion jetzt allerdings blockierend geschrieben:

while(myIndex < messageLength)
{
    if(Serial.available() > 0)
    {
	inData[myIndex] = Serial.read();
	myIndex++;
     }
}

Wenn dich das nicht stört ok, aber es kann auch Nachteile haben wenn der Rest des Codes auf die Übertragung warten muss. Das sind je nach Baudrate aber auch nur ein paar ms. Kommt auf die Anwendung ab ob das vertretbar ist.