Массив с плавающей точкой в ​​байтах преобразуется в ноль - PullRequest
0 голосов
/ 26 марта 2019

Я пытаюсь непрерывно отправить массив из 6 чисел с плавающей точкой по протоколу MQTT. Я отправлял их как символы ASCII, используя функцию sprintf. Я решил отправить их как необработанные байты. Я поместил эти поплавки в союз, чтобы представлять их как беззнаковый символ. Проблема в том, что когда любое из этих чисел с плавающей запятой имеет целочисленное значение, их байтовое представление становится нулевым после позиции целого числа.

union {
    float array[6];
    unsigned char bytes[6 * sizeof(float)];
} floatAsBytes;

Если все floatAsBytes.array состоят из значений с плавающей точкой, проблем вообще не возникает.

Если я скажу floatAsBytes.array[0] = 0, floatAsBytes.bytes станет нулевым.

Если я скажу floatAsBytes.array[3] = 4, я смогу увидеть первые 8 байтов, но на этот раз последние 16 байтов станут нулевыми.

Минимальный пример моего C-кода на стороне клиента

#define QOS 0
#define TIMEOUT     1000L

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include "MQTTClient.h"

union bitFloat{
    float f[6];
    unsigned char s[6*sizeof(float)];
};

void publish(MQTTClient client, char* topic, char* payload) {
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    pubmsg.payload = payload;
    pubmsg.payloadlen = strlen(pubmsg.payload);
    pubmsg.qos = QOS;
    pubmsg.retained = 0;
    MQTTClient_deliveryToken token;
    MQTTClient_publishMessage(client, topic, &pubmsg, &token);
    MQTTClient_waitForCompletion(client, token, TIMEOUT);
}

int main(){

    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;
    int rc;

    MQTTClient_create(&client, "MQTTADDRESS:MQTTPORT", "TestClient",
                        MQTTCLIENT_PERSISTENCE_NONE, NULL);
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;

    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        exit(-1);
    }

    int i;

    while(1){
        union bitFloat payload;
        payload.f[0] = 4.53; payload.f[1] = 2.39; payload.f[2] = 28.96; payload.f[3] = -1.83; payload.f[4] = -27.0; payload.f[5] = 9.32;
        publish(client, "MyTestTopic", payload.s);
        sleep(1);
    }
    return 0;
}

Python-скрипт для получения и отображения сообщений

# !/usr/bin/env python

import struct
import numpy as np
import paho.mqtt.client as mqtt

def on_message(client, userdata, message):
    test1 = struct.unpack('<f', message.payload[0:4])[0]
    test2 = struct.unpack('<f', message.payload[4:8])[0]
    test3 = struct.unpack('<f', message.payload[8:12])[0]
    test4 = struct.unpack('<f', message.payload[12:16])[0]
    test5 = struct.unpack('<f', message.payload[16:20])[0]
    test6 = struct.unpack('<f', message.payload[20:24])[0]
    print(test1, test2, test3, test4, test5, test6)

client = mqtt.Client()
client.on_message = on_message

client.connect("MQTTADDRESS", MQTTPORT)
client.subscribe("MyTestTopic")

client.loop_forever()

Ответы [ 2 ]

1 голос
/ 26 марта 2019

Эта строка

pubmsg.payloadlen = strlen(pubmsg.payload);

неверна.Вы используете strlen для чего-то, что не является строкой.Из-за использования strlen длина будет неправильной, так как strlen будет считаться только до тех пор, пока не увидит нулевой байт.

Пример: Рассмотрим payload.f[0] = 1;.Двоичное представление 1.0 равно 3f800000

В системах с прямым порядком байтов это будет сохранено как 00 00 80 3f, поэтому использование strlen приведет к 0.

В системах с прямым порядком байтов это будет сохраненокак 3f 80 00 00, так что использование strlen приведет к 2.

Другими словами - strlen неправильная функция.

Возможно, вам нужно

pubmsg.payloadlen = 6 * sizeof(float);
0 голосов
/ 26 марта 2019

Код работает как положено.

Вот Минимально завершенный проверяемый пример . Я предполагаю, что вы что-то делаете правильно. Если вы предоставите свой собственный код, это может показать, что я неправильно понял ваш вопрос:

#include <stdio.h>

int main()
{

  union {
      float array[6];
      unsigned char bytes[6 * sizeof(float)];
  } floatAsBytes;

  // load up array with some date
  for(int i = 0; i < 6; i++) {
    floatAsBytes.array[i] =  1.99 + i;
  }

  puts("\nfirst run:");

  floatAsBytes.array[0] = 0;

  // dump array
  for(int i = 0; i< 6; i++) {
    printf("float #%d: %f\n", i, floatAsBytes.array[i]);

  }

  // dump bytes
  for(int i = 0; i < sizeof(float)*6; i++) {
    if(i % sizeof(float) == 0)
      printf("\n");
    printf(" %2x",floatAsBytes.bytes[i]);
  }

  // second example
  puts("\nSecond run:");

  floatAsBytes.array[3] = 4;
  // dump array
  for(int i = 0; i< 6; i++) {
    printf("float #%d: %f\n", i, floatAsBytes.array[i]);

  }

  // dump bytes
  for(int i = 0; i < sizeof(float)*6; i++) {
    if(i % sizeof(float) == 0)
      printf("\n");
    printf(" %2x",floatAsBytes.bytes[i]);
  }
  return 0;
}

Вот вывод:

first run:
float #0: 0.000000
float #1: 2.990000
float #2: 3.990000
float #3: 4.990000
float #4: 5.990000
float #5: 6.990000

  0  0  0  0
 29 5c 3f 40
 29 5c 7f 40
 14 ae 9f 40
 14 ae bf 40
 14 ae df 40
Second run:
float #0: 0.000000
float #1: 2.990000
float #2: 3.990000
float #3: 4.000000
float #4: 5.990000
float #5: 6.990000

  0  0  0  0
 29 5c 3f 40
 29 5c 7f 40
  0  0 80 40
 14 ae bf 40
 14 ae df 40
Process finished with exit code 0

Я не вижу поведения, которое вы описываете. Код работает как положено.

...