#java #arduino #processing #simulink
Вопрос:
ниже приведен код от arduino:
typedef union{
float number;
uint8_t bytes[4];
} FLOATUNION_t;
FLOATUNION_t myValue;
float getFloat(){
int cont = 0;
FLOATUNION_t f;
while (cont < 4 ){
f.bytes[cont] = Serial.read() ;
cont = cont 1;
}
return f.number;
}
есть ли какая-либо альтернатива этому для программирования обработки(java)?
полностью мне нужен код, который получает двоичные данные из последовательного порта, и я хочу, чтобы эти данные были в виде float в программировании обработки.
первоначально я отправляю некоторые данные через блок последовательной отправки в Simulink для программирования обработки, я использую библиотеку processing.serial , но я не смог получить эти биты и преобразовать их в float или int.
Ответ №1:
Вам нужно :
- буферизуйте каждый байт за раз от Simulink до обработки: Последовательные
buffer()
/serialEvent()
/readBytes(bytesFromSimulink)
могут хорошо работать в тандеме здесь - упакуйте байты в строку int (при необходимости сдвигая байты) и
OR
вставьте их:int intBits = bytesFromSimulink[3] << 24 | bytesFromSimulink[2] << 16 | bytesFromSimulink[1] << 8 | bytesFromSimulink[0];
- преобразуйте значение int в значение с плавающей точкой с помощью
Float.intBitsToFloat()
:floatFromSimulink = Float.intBitsToFloat( intBits );
Вот основной набросок, иллюстрирующий вышеприведенные идеи:
import processing.serial.*;
// how many bytes are expecting sent in one go
final int SERIAL_BUFFER_SIZE = 4;
// pre-allocate serial read buffer
byte[] bytesFromSimulink = new byte[SERIAL_BUFFER_SIZE];
// float from bytes
float floatFromSimulink;
// serial port reference
final String PORT_NAME = "COM2";
final int BAUD_RATE = 115200;
Serial simulinkPort;
void setup(){
size(300, 300);
try{
simulinkPort = new Serial(this, PORT_NAME, BAUD_RATE);
// only fire serialEvent() when the right number of bytes has been buffered
simulinkPort.buffer(SERIAL_BUFFER_SIZE);
}catch(Exception e){
println("error opening serial port(" PORT_NAME "): double check the port name, wiring and make sure the port isn't already open in another application");
e.printStackTrace();
exit();
}
}
void draw(){
background(0);
// format bytes to hex and float to 2 decimal places
text(String.format("hex: %snfloat: %.2f", hex(byteFromSimulink), floatFromSimulink),
10, 15);
}
void serialEvent(Serial port) {
port.readBytes(bytesFromSimulink);
// pack bytes into a 32bit int (shifting each byte accordingly): double check the byte order (e.g. LSB / MSB)
int intBits = bytesFromSimulink[3] << 24 |
bytesFromSimulink[2] << 16 |
bytesFromSimulink[1] << 8 |
bytesFromSimulink[0];
// convert int to to float
floatFromSimulink = Float.intBitsToFloat( intBits );
}
// pretty-print byte array
String hex(byte[] data){
String output = "";
for(byte singleByte : data){
output = hex(singleByte) ' ';
}
return output;
}
Надеюсь, вышесказанное просто работает, но имейте в виду, что это непроверенный код.
Есть две вещи, которые, как мне кажется, могут пойти не так:
- Байты поступают не в правильном порядке. (допустим, Simulink непрерывно передает последовательные данные, но обработка начинается позже и улавливает данные только со 2-го, 3-го или 4-го байта вместо первого: данные будут смещены). Вы можете попытаться удалить
buffer()
/serialEvent()
с помощью цикла блокировки и получать по одному байту за раз (напримерif(simulinkPort.available() >= 1) myNewByte = simulinkPort.read();
) и подсчет / упаковка байтов в массив байтов вручную. Вы также можете попробовать подход «вызов/ответ»: например, Simulink не отправляет никаких данных, пока не получит один символ из обработки (скажем, «A»), а затем начнет потоковую передачу, поэтому обработка готова буферизировать 4 байта за раз с самого начала. - Я не уверен, в каком порядке байты отправляются из simulink: выше я предполагаю, что справа налево, но все наоборот, просто поменяйтесь индексами:
int intBits = byteFromSimulink[0] << 24 | byteFromSimulink[1] << 16 | byteFromSimulink[2] << 8 | byteFromSimulink[3];
Другая ошибка в Java/обработке заключается в том, что байты составляют от -127 до 127, поэтому при проверке вам понадобятся байты маски: println(myByte amp; 0xFF);
Основываясь на предложении g00se в комментариях ниже, вот попытка ByteBuffer
выбора:
import java.nio.ByteBuffer;
import processing.serial.*;
// how many bytes are expecting sent in one go
final int SERIAL_BUFFER_SIZE = 4;
// pre-allocate serial read buffer
ByteBuffer bytesFromSimulink;
// float from bytes
float floatFromSimulink;
// serial port reference
final String PORT_NAME = "COM2";
final int BAUD_RATE = 115200;
Serial simulinkPort;
void setup(){
size(300, 300);
try{
simulinkPort = new Serial(this, PORT_NAME, BAUD_RATE);
// only fire serialEvent() when the right number of bytes has been buffered
simulinkPort.buffer(SERIAL_BUFFER_SIZE);
bytesFromSimulink = ByteBuffer.allocate(SERIAL_BUFFER_SIZE);
}catch(Exception e){
println("error opening serial port(" PORT_NAME "): double check the port name, wiring and make sure the port isn't already open in another application");
e.printStackTrace();
exit();
}
}
void draw(){
background(0);
// format bytes to hex and float to 2 decimal places
text(String.format("hex: %snfloat: %.2f", hex(bytesFromSimulink), floatFromSimulink),
10, 15);
}
void serialEvent(Serial port) {
// pass new data to the byte buffer
bytesFromSimulink.put(port.readBytes(SERIAL_BUFFER_SIZE));
// set the index back to 0
bytesFromSimulink.rewind();
// read the first (rewinded) 4 bytes as a float
floatFromSimulink = bytesFromSimulink.getFloat();
}
// pretty-print byte array
String hex(ByteBuffer data){
String output = "";
for(int i = 0 ; i < data.limit(); i ){
output = hex(data.get(i)) ' ';
}
return output;
}
Комментарии:
1. Возможно, вы найдете методы ByteBuffer немного более удобными. Также возможно изменение порядка байтов
2. Оооо! Отличная идея: пакет java.nio содержит множество более быстрых утилит, а
ByteBuffer
такжеgetFloat()
идеально подходит для этого.3. Я попробовал ваш код и, например: ввод в Simulink был 25, а вывод вашего кода в двоичном формате выглядит так : 11001000000000000000000000000, но двоичный формат 25 равен 00000000000000000000000000011001
4. вышеуказанная проблема была решена, я только что изменил LittleEndian на BigEndian, и теперь порядок битов верен. но я хочу использовать это значение для предстоящих вычислений, как я могу изменить это шестнадцатеричное значение на целое или с плавающей точкой?
5. Это отличная новость: похоже, данные поступают правильно, просто нужно поменять порядок байтов. Если вы используете эту
byte[]
опцию, просто поменяйте местами индексы, добавленные выше. Если вы используете этуByteBuffer
опцию, попробуйте установитьorder()
правильный тип конца.