0
votes

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()
2
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
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
Ok, then that is not the problem. <g> - Rudy Velthuis
Could you provide a test sample where the unexpected behavior occurs ? - Jona

2 Answers

1
votes

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);
0
votes

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.