получать данные в виде плавающей точки при программировании обработки с последовательного порта

#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:

Вам нужно :

  1. буферизуйте каждый байт за раз от Simulink до обработки: Последовательные buffer() / serialEvent() / readBytes(bytesFromSimulink) могут хорошо работать в тандеме здесь
  2. упакуйте байты в строку int (при необходимости сдвигая байты) и OR вставьте их: int intBits = bytesFromSimulink[3] << 24 | bytesFromSimulink[2] << 16 | bytesFromSimulink[1] << 8 | bytesFromSimulink[0];
  3. преобразуйте значение 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;
}
 

Надеюсь, вышесказанное просто работает, но имейте в виду, что это непроверенный код.
Есть две вещи, которые, как мне кажется, могут пойти не так:

  1. Байты поступают не в правильном порядке. (допустим, Simulink непрерывно передает последовательные данные, но обработка начинается позже и улавливает данные только со 2-го, 3-го или 4-го байта вместо первого: данные будут смещены). Вы можете попытаться удалить buffer() / serialEvent() с помощью цикла блокировки и получать по одному байту за раз (например if(simulinkPort.available() >= 1) myNewByte = simulinkPort.read(); ) и подсчет / упаковка байтов в массив байтов вручную. Вы также можете попробовать подход «вызов/ответ»: например, Simulink не отправляет никаких данных, пока не получит один символ из обработки (скажем, «A»), а затем начнет потоковую передачу, поэтому обработка готова буферизировать 4 байта за раз с самого начала.
  2. Я не уверен, в каком порядке байты отправляются из 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() правильный тип конца.