Hallo zusammen,
ich hoffe diese Frage wurde noch nicht allzu oft beantwortet. Ich suche ein Beispiel im "Arduinostyle".
Ich möchte 6 float-Werte in ein Byte.array packen, um es dann an ein Processingprogramm zu via Serial.write senden. Umgekehrt habe ich es u.a. mit ByteBuffer geschafft.
Vielen Dank im Voraus
Kucky
Float hat 4 Byte. Das sollte also genau wie ein long zu wandeln gehen:
void floatToByte(byte* arr, float value)
{
long l = *(long*) &value;
arr[0] = l & 0x00FF;
arr[1] = (l >> 8) & 0x00FF;
arr[2] = (l >> 16) & 0x00FF;
arr[3] = l >> 24;
}
void loop()
{
byte arr[4];
float test = 1.234;
floatToByte(arr, test);
}
Der Trick dabei ist die Binär-Darstellung des Floats in einem int/long zu erhalten. Das habe ich mir hier abgeguckt:
Dabei wird die Adresse des floats auf einen long pointer gecastet und dann dereferenziert. Als Alternative kann man dazu auch eine Union nehmen.
In Visual C++ funktioniert das. Hier der Test Code:
#include "stdafx.h"
#include <iostream>
#include <bitset>
using namespace std;
void floatToByte(unsigned char* arr, float value)
{
int i = *(int*) &value;
arr[0] = i & 0x00FF;
arr[1] = (i >> 8) & 0x00FF;
arr[2] = (i >> 16) & 0x00FF;
arr[3] = (i >> 24);
}
float byteToFloat(unsigned char* arr)
{
int i = arr[0] | (arr[1] << 8) | (arr[2] << 16) | (arr[3] << 24);
return *(float*) &i;
}
int _tmain(int argc, _TCHAR* argv[])
{
unsigned char arr[4];
float test = 6.6666;
floatToByte(arr, test);
int i = *(int*) &test;
cout << test << endl << bitset<32>(i) << endl;
cout << bitset<8>(arr[3]) << bitset<8>(arr[2]) << bitset<8>(arr[1]) << bitset<8>(arr[0]) << endl;
cout << byteToFloat(arr) << endl;
char c;
cin >> c;
return 0;
}
Vielen vielen Dank,
werde ich ausprobieren und dann melden.
LG
Kucky
@serenifly: Hallo, da brauchst Du gar keine Tricks
Hallo,
wie schon erwähnt, ist der richtige Datentyp für solche Umwandlungen in C++ die Union. Hier ein Sketch, der das Gewünschte leistet:
void setup() {
// Serielle Schnittstelle mit 9600bps initialisieren
Serial.begin(9600);
}
void loop()
{
union speicherTeiler {
// lang enthält den Wert Float-Zahl
float lang;
struct {
byte b1;
byte b2;
byte b3;
byte b4;
} geteilt;
// Die Struktur geteilt teilt sich den Speicherplatz
// mit der float Variablen, b1 bis b4 enthalten also den Wert
// von lang als 4 einzelne Bytes
};
// Eine Variable vom Typ speicherTeiler definieren
union speicherTeiler st;
// Alle Bits in st.lang auf 1
st.lang = 3.14f;
// Wert von st.lang ausgeben
Serial.print("st.lang: ");
Serial.println(st.lang);
// Wert von st.geteilt.kurz1 ausgeben
Serial.print("st.geteilt.b1: ");
Serial.println(st.geteilt.b1);
Serial.print("st.geteilt.b2: ");
Serial.println(st.geteilt.b2);
Serial.print("st.geteilt.b3: ");
Serial.println(st.geteilt.b3);
Serial.print("st.geteilt.b4: ");
Serial.println(st.geteilt.b4);
// 3 Sekunden warten, bevor loop() erneut durchlaufen wird
delay(3000);
}
Zur Erklärung wie das genau funktioniert verweise ich auf den C++-Kurs.
Die Rückwandlung geht entsprechend: Vier sinnvolle Bytes in die Union-Members b1 bis b4 eintragen und dann enthält st.lang die Fließkommazahl.
Gruß,
Ralf
PS: Das funktioniert natürlich auch in Visual C++ und allen anderen C- und C++-Dialekten.
Habe ja noch hingeschrieben, dass man auch Unions nehmen kann
Der Vorteil das in ein richtiges Byte-Array zu verpacken statt ein struct liegt darin, dass man dann mit einer for-Schleife sehr schön darüber iterieren kann. Vor allem wenn man das von einem Float Wert auf 6 erweitert. Dann muss man nur das Ziel-Array 24 Bytes groß machen und die Indizes anpassen (4 * i + x). Man kann dann ein Float-Array übergeben und bekommt ein Byte-Array zurück. Und Serial.write() schluckt Byte-Arrays in einem Befehl, wenn man die Größe mit angibt.
Alles in Unions zu machen belegt natürlich weniger Speicher, da man die Werte nicht in zwei Darstellungs-Arten abspeichern muss. Aber das ist bei den paar Bytes nicht so kritisch. Letztlich Geschmackssache
Oder man macht es so ähnlich:
union memory
{
float floats[6];
byte bytes[24];
};
memory values;
Dann schreibt man die 6 Float Werte rein und kann das Array direkt mit Serial.write(values.bytes, 24) verschicken
Serenifly:
Habe ja noch hingeschrieben, dass man auch Unions nehmen kann
Aber nur ganz klein und ganz weit hinten auf meinem Bildschirm. Deswegen gilt das nicht richtig. ]
Gruß,
Ralf
Vielen Dank für alle Antworten. Ich habe ein wenig Erfahrung mit C/C++, aber sobald es um Serial... und Bytes geht, steh ich auf dem Schlauch.
Brauche etwas um das alles umzusetzten, aber dann melde ich mich.
LG
Kucky
Also, ich habe gesehen, dass die 4 Zahlen (195, 245, 72 und 64) einem 4*8 Bitmuster entsprechen. Warum gerade diese Zahlen, ist mir im Moment nicht klar. Aber da kommt noch :~. Vorab aber noch 2 Fragen bitte:
- Welchen Werte trage ich bei Serial.write(x, y) ein?
- Wie kann ich mehrere Werte in ein Bitmuster bekommen? Oder wäre dies ein Bytearray von "union speicherTeiler st[6];"?
LG Kucky
- Welchen Werte trage ich bei Serial.write(x, y) ein?
Siehe hier:
http://arduino.cc/en/Serial/Write
x ist das Byte Array. y ist die Anzahl der Bytes
Wie kann ich mehrere Werte in ein Bitmuster bekommen? Oder wäre dies ein Bytearray von “union speicherTeiler st[6];”?
Das würde gehen. Aber praktischer ist meiner Meinung nach das:
union memory
{
float floats[6];
byte bytes[24];
};
memory values;
values.floats[0] = ....;
values.floats[1] = ....;
....
Es sei den du willst die jeweiligen Bytes die zu einem Float gehören aus irgendeinem Grund getrennt behandeln. Dann ist ein Array aus Unions wiederum besser.
Bei der anderen Lösung sollte das gehen:
void floatToByte(byte* arr, float* values)
{
for(int i = 0; i < 6; i++)
{
long l = *(long*) &values[i];
arr[i * 4] = l & 0x00FF;
arr[i * 4 + 1] = (l >> 8) & 0x00FF;
arr[i * 4 + 2] = (l >> 16) & 0x00FF;
arr[i * 4 + 3] = l >> 24;
}
}
Und dann Arrays von Größe 24 und 6 übergeben. Das Verunden ist übrigens glaube ich gar nicht nötig, da da eine Typumwandlung von long nach byte erfolgt.
Sorry, Frage falsch gestellt:
Was wäre in Deinem Beispiel das "Byte-Array"? Die Anzahl ist wohl 4.
Den 2ten Teil muss ich sacken lassen.
LG
Kucky
Wenn du den Code von Schachmann hast, dann hast du gar kein Byte Array. Der hat die Bytes in einem struct anlegt. Die müsstest du dann einzeln verschicken.
Wenn du eine Union pro Float mit Byte Array hast:
union memory
{
float fl;
byte bytes[4];
};
union memory value;
Dann geht das verschicken einfach so:
Serial.write(value.bytes, 4);
Oder wenn du die 4 Floats in eine Union packst:
union memory
{
float floats[4];
byte bytes[16];
};
union memory values;
Serial.write(values.bytes, 16);
Die Anzahl ist eigentlich egal. Das Byte Array muss nur 4 mal so groß wie das Float Array sein. Man kann das auch schön so machen:
#define NUM_VALUES 4
#define NUM_BYTES (NUM_VALUES * 4)
union memory
{
float floats[NUM_VALUES];
byte bytes[NUM_BYTES];
};
union memory values;
Dann muss man nur die Konstante NUM_VALUES ändern und alles andere passt sich automatisch an. Bei Serial kannst du dann auch Serial.write(values.bytes, NUM_BYTES) machen und es passt sofort.
EDIT:
Sorry. Da war noch ein Fehler bei Serial drin. Muss natürlich "values.bytes" heißen. Beim ersten mal hatte ich noch richtig gemacht. Das ist eigentlich genau die Stelle um die es ging
Ich glaube jetzt sehe ich schon viel klarer. Ist wahrscheinlich ganz einfach, wenn man es einmal verstanden hat. Werde dies nun für meine Zwecke ausprobieren.
Vielen Dank für die Hilfe und die Geduld.
LG
Kucky
EDIT Gesehen, danke.
Habe ich es hinbekommen?
#define NUM_VALUES 9
#define NUM_BYTES (NUM_VALUES * 4)
union memory{
byte asBytes[NUM_BYTES];
float asFloat[NUM_VALUES];
};
union memory sendToArduino;
// 0 - 3: float Xangle
// 4 - 7: float Yangle
// 8 - 11: float Zangle
// 12 - 15: float Altitude
// 16 - 19: float Temperature
// 20 - 23: float Rpses0
// 24 - 27: float Rpses1
// 28 - 31: float Rpses2
// 32 - 35: float Rpses3
void setup()
{
sendToArduino.asBytes[0] = 12.;
sendToArduino.asBytes[1] = 3.4;
sendToArduino.asBytes[2] = 0;
sendToArduino.asBytes[3] = 52.0;
sendToArduino.asBytes[4] = 24.1;
sendToArduino.asBytes[5] = 555;
sendToArduino.asBytes[6] = 558;
sendToArduino.asBytes[7] = 556;
sendToArduino.asBytes[8] = 559;
}
void loop()
{
Serial.write(sendToArduino.asBytes, NUM_BYTES);
}
Kucky
sendToArduino.asBytes[0] = 12.; <— da kommt wahrscheinlich ein Fehler
Ansonsten sollte theoretisch funktionieren. Musst halt mal schaun was am anderen Ende ankommt
In loop solltest du allerdings ein Delay einbauen, damit Zeit ist den Ausgangspuffer zu leeren.
Den Fehler habe ich schon korrigiert, danke. Im richtigen Progi sollte Serial.flush das wohl erledigen, denke ich.
Kucky
Hey,
also aus meiner Sicht funktioniert es. Ich glaube, ich habe es auch verstanden. Die Ausgabe in VS per Serial.print lautet:
asBytes[0]: 12
asBytes[1]: 3
asBytes[2]: 0
asBytes[3]: 52
asBytes[4]: 24
asBytes[5]: 43
asBytes[6]: 46
asBytes[7]: 44
asBytes[8]: 47
Dieses Processingprogi macht aber seltsames:
import processing.serial.*;
import java.nio.ByteBuffer;
Serial myPort;
void setup() {
println(Serial.list());
myPort = new Serial(this, Serial.list()[1], 9600);
myPort.write(65); // Only for test
}
void draw() {
// Expand array size to the number of bytes you expect
byte[] inBuffer = new byte[8];
while (myPort.available() > 0) {
inBuffer = myPort.readBytes();
myPort.readBytes(inBuffer);
if (inBuffer != null) {
println(inBuffer);
}
}
}
Die Augabe lt.:
[0] 12
[1] 3
[0] 52
[1] 24
[2] 43
[3] 46
[4] 44
[5] 47 ← Bis hierhin OK, aber falscher Index
Dann wird´s für mich “seltsam”
[0] 13
[1] 10
[2] 97
[3] 115
[4] 66
[5] 121
[6] 116
[7] 101
[8] 115
[9] 91
[10] 48
Könnte ihr hier nochmal bitte helfen? Ich meine in Bezug auf convertieren nach float.
Gruß
Kucky
Glaube da ist noch ein grundlegender Fehler drin, den ich übersehen hatte:
void setup()
{
sendToArduino.asBytes[0] = 12.;
sendToArduino.asBytes[1] = 3.4;
sendToArduino.asBytes[2] = 0;
sendToArduino.asBytes[3] = 52.0;
sendToArduino.asBytes[4] = 24.1;
sendToArduino.asBytes[5] = 555;
sendToArduino.asBytes[6] = 558;
sendToArduino.asBytes[7] = 556;
sendToArduino.asBytes[8] = 559;
}
Du musst diese Werte in asFloat eintragen! Und dann das asBytes Array verschicken
Dann ist der Eingangspuffer zu klein um mehr als 8 Byte einzulesen:
byte inBuffer = new byte[8]
Und du musst die Werte auf der Empfangsseite wieder in Float konvertieren (das kannst du auf die gleiche Art machen nur anders herum). Oder aus Serial direkt Float auslesen wenn das geht.
Habe ich geändert. In habe es nun mit 9*4 Byte probiert. Es kann doch nur 9 oder 36 sein oder?
[0] -102
[1] -103
[2] 65
[0] 65
[1] -102
[2] -103
[3] 89
[4] 64
[5] 80
[6] 66
[7] -51
[8] -52
[9] -64
[0] 65
[1] -64
[2] 10
[3] 68
Wieder stimmt der Index nicht. Wie gesagt, wenn ich einmal den richtigen Weg sehe, ist alles gut.
In Java gibt es kein "union", muss ich also anders hinbekommen.
Gruß
Kucky
In Java gibt es kein "union", muss ich also anders hinbekommen.
--> java.nio.FloatBuffer