1

I am trying to continuously send array of 6 floats over MQTT protocol. I was sending them as ASCII characters using sprintf function. I decided to send them as raw bytes. I put these floats into a union to represent them as unsigned char. The problem is when any of these floats is integer value, their byte representation becomes null after the position of integer.

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

If all of floatAsBytes.array consist float values, there is no problem at all.

If I say floatAsBytes.array[0] = 0, floatAsBytes.bytes becomes null.

If I say floatAsBytes.array[3] = 4, I can see first 8 bytes however this time last 16 bytes becomes null.

Minimal example of my client side C-code

#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 script to receive messages and display them

# !/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()
Şener
  • 25
  • 4
  • This may have to do with endianness, or network byte order. Check which bytes you get and which bytes you send and which endianness is used on either end. – Rudy Velthuis Mar 26 '19 at 08:32
  • Byte order is little-endian. Also before sending them, I am checking it with printf("Byte representation: %s\n", floatAsBytes.bytes). It is null on both client and server side. @RudyVelthuis – Şener Mar 26 '19 at 08:36
  • Ok, then that is not the problem. – Rudy Velthuis Mar 26 '19 at 08:37
  • Possible duplicate of [C++ Union of a Float and a Byte Array Issue](https://stackoverflow.com/questions/52122263/c-union-of-a-float-and-a-byte-array-issue) – M.Winkens Mar 26 '19 at 08:40
  • Is this perhaps interpreted as text with a 0 byte terminating the "string"? – Rudy Velthuis Mar 26 '19 at 08:40
  • Please include a main() function and provide a [Compilable, Minimal, Complete, and Verifiable Example](https://stackoverflow.com/help/mcve). This helps us help you. By providing a main() function, it saves time for the person trying to duplicate your results and help you. – Gardener Mar 26 '19 at 08:56
  • 2
    Could you provide a test sample where the unexpected behavior occurs ? – Jona Mar 26 '19 at 09:13
  • Quote: `floatAsBytes.array[0] = 0, floatAsBytes.bytes becomes null.` Well, the binary representation of the value float `0.0` is all-zero... – Support Ukraine Mar 26 '19 at 09:50
  • Please [edit] your question and show a [mcve] as well as expected vs. actual output. – Jabberwocky Mar 26 '19 at 09:54
  • @4386427 I've tried it using different values such as 3,5,40 – Şener Mar 26 '19 at 10:51
  • Can't check this right now, but what happens if you do: `payload.f[0] = 4.53f; payload.f[1] = 2.39f; etc...` (note the f for float). It should not make a big difference, but hey, I don't know another reason why it shouldn't work. Also, look at the byte values in a debugger before you send them. What are they? – Rudy Velthuis Mar 26 '19 at 16:33
  • It wasn't a data related problem. Solution is well explained in the answer above. @RudyVelthuis – Şener Mar 27 '19 at 08:09
  • @Şener: the answer is below, and it was something I already expected (before you posted all that code: "Is this perhaps interpreted as text with a 0 byte terminating the 'string'"). Exactly that is what *strlen()* did. So it was a data releated problem: you treated binary data as if it were text. – Rudy Velthuis Mar 27 '19 at 08:47

2 Answers2

2

This line

pubmsg.payloadlen = strlen(pubmsg.payload);

is wrong. You are using strlen on something that isn't a string. Due to use of strlen the length will be wrong as strlen only count til it sees a byte that are zero.

Example: Consider payload.f[0] = 1;. The binary representation of 1.0 is 3f800000

On little endian systems this will be saved as 00 00 80 3f so using strlen will result in 0.

On big endian systems this will be saved as 3f 80 00 00 so using strlen will result in 2.

In other words - strlen is the wrong function.

You probably need

pubmsg.payloadlen = 6 * sizeof(float);
Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
0

The code works as expected.

Here is a Minimal Complete Verifiable Example. I am guessing that you are doing something right this. When you provide your own code, it may show that I have misunderstood your question:

#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;
}

Here is the output:

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

I am not seeing the behavior that you are describing. The code works as expected.

Gardener
  • 2,591
  • 1
  • 13
  • 22
  • No. The OP never provided his code. I think that the code would have made this problem clear. I provided what I understood of his code. His statement, "The problem is when any of these floats is integer value, their byte representation becomes null after the position of integer." is now more understandable. – Gardener Mar 26 '19 at 14:04
  • what I mean is: you didn't know his code, but you find that your code works fine. That's cool, but since you didn't know what he is really doing, the fact that your code works fine is not so relevant, IMO. – Rudy Velthuis Mar 26 '19 at 16:30
  • I agree with you to a certain extent. However, in many cases, I see questions that simply lack a mcve. If the OP would post it, the problem would be solved. As a question answerer, I can make queries, that often go unanswered, or I can illustrate to the OP what an MVCE might look like. By doing this, I may be able to educate the OP into disclosing the true issues, or I may solve the problem. I take the hit by risking an answer that is not exactly in line, but I think it often helps. See the intro to my answer. – Gardener Mar 26 '19 at 17:02