Two bytes to floating point number

Status
Not open for further replies.

Tady

Well-known member
Hello

I have a nework analyzer that uses modbus communication. Now I have a problem reading the measured value.

It seems the meter is using two 16bit bytes for each value.
Currently I'm trying to read only Power
So if the measured power consumption is less than 1000W, say 136.71W the first byte is 65024 (decimal) and the second is 13671(decimal).
But if the power goes over 1000W.. say 2034.2W the first byte is 65280 and the second is 20342

So the first byte represents the decimal place and the second the actual value..

Can someone help me how to calculate this so I get actual values in float?

Thank you!
 
If the storage format is a standard float maybe a union will let it be assembled?

Without actually looking at correct syntax - something like this?
Code:
typedef union
{
  struct
  {
    uint16_t one;
    uint16_t two;
  } b;
  float f;
} aFloat_Type;

Once it compiles a quick test will show which half is which - and if it gives a valid float in the end.
 
Just for the sake of correctness.
There is no such thing as a 16bit byte. A byte is 8bits. We are talking about words which are 2 bytes long.

If you calculate the 2's complement of word1 and divide by 256, this apparently results in the number of decimal places, word1dp.
Then it is just a matter of dividing word2 by 10 to the power of word1dp to get the float.
 
Hmmm.. I will try unon in the morning but i think it will not work.
I did some digging and I think I asked the qustion wrong.

I don't get two bytes but I get two Ints.
And I don't know how the uion function is going to work since the secod int already gives me the exact number only without the decimal.

I did some tests and this is what I got:

If the load is 4.507W I get 65024 in the first int and 4507 in the second
if the load is 46.61W i get 65024 in the first int and 4616 in the second
if the load is 1938.2W I get 65280 in the first int and 19382 in the second

I can't load it over 10kW :) So this makes me think the first int only tells me where the decimal point should be but I cant figure it out how to calculate it.

65024 = 1111111000000000
65280 = 1111111100000000

so maybe for 10KW or more could be 65408 (1111111110000000).
 
@neurofun I think you are right!

2's complement of 65024 is 512 and if I divide by 256 i get 2
2's complement od 65280 is 256 and if I divide by 256 i get 1

How can I calculate the 2's complement I used an onlinecalculatur for this :) invert all 1'sand 0's and add 1?
 
Does the manufacturer of this power meter publish any guidance on how to use the data they provide? Maybe share that info here?
 
It's an Iskra MC666 netwok analyzer. I couldn't ind any data regarding the modbus communication. They do provide windows software for configuration anl live data view. Nothing more. I sniffed the bus and got the register locations but that is about it..

I think neurofun is on th right track here.. I need to change my code so I can test
 
As @neurofun notes - the number ref names were not correct. But seeing this:
the first byte is 65024 (decimal) and the second is 13671(decimal).
But if the power goes over 1000W.. say 2034.2W the first byte is 65280 and the second is 20342

Implied your 'byte' is a 16 bit word of two bytes - or it could not hold those values.

And if the source of these 4 bytes - in two 16 bit parts is a common float representation just divided for transmission, then the union might allow them to be re-assembled.

Working backward SerMon shows this to see what p#2 suggested, which does not match post #1, so those don't appear to be 'float' halves.
Code:
xx.b.one46531
xx.b.two17160
yy.b.one18022
yy.b.two17662

Updated for two of the values in p#4:
if the load is 46.61W i get 65024 in the first int and 4616 in the second
if the load is 1938.2W I get 65280 in the first int and 19382 in the second

It like this::
Code:
typedef union
{
  struct
  {
    uint16_t one;
    uint16_t two;
  } b;
  float f;
} aFloat_Type;

void loop3() {
  aFloat_Type xx;
  xx.f = 1938.2;
  aFloat_Type yy;
  yy.f = 46.61;

  Serial.print( "xx.f" );
  Serial.println( xx.f );
  Serial.print( "xx.b.one" );
  Serial.println( xx.b.one );
  Serial.print( "xx.b.two" );
  Serial.println( xx.b.two );

  Serial.print( "yy.f" );
  Serial.println( yy.f );
  Serial.print( "yy.b.one" );
  Serial.println( yy.b.one );
  Serial.print( "yy.b.two" );
  Serial.println( yy.b.two );
}

Shows this - pardon the spacing:
Code:
xx.f1938.20
xx.b.one18022
xx.b.two17650
yy.f46.61
yy.b.one28836
yy.b.two16954
 
@defragster I will try your method in the morning so I can see and learn. But I did a quick test with nerofun's post and it works perfectly. I don't know why this method is used (i didn't com across it after tons of googling) but it seem that this is the case here. But I will thy the union method that you suggested.

Thank you!
 
@defragster I will try your method in the morning so I can see and learn. But I did a quick test with nerofun's post and it works perfectly. I don't know why this method is used (i didn't com across it after tons of googling) but it seem that this is the case here. But I will thy the union method that you suggested.

Thank you!

What I did is C fun - but solution to the wrong problem given guess from original post.
 
How can I calculate the 2's complement I used an onlinecalculatur for this :) invert all 1'sand 0's and add 1?
Yes
I did some tests and this is what I got:

If the load is 4.507W I get 65024 in the first int and 4507 in the second
if the load is 46.61W i get 65024 in the first int and 4616 in the second
if the load is 1938.2W I get 65280 in the first int and 19382 in the second
Hmm, the first line doesn't make sense. word1 should be 64768.
I don't know why this method is used (i didn't com across it after tons of googling) but it seem that this is the case here.
Maybe some kind of obfuscation?
 
The format of the data are explained in Appendix A of the manual on page 77.
Data type T5 - Unsigned Measurement (32 bit):
bits # 31...24 Decade Exponent (Signed 8 bit)
bits # 23...00 Binary Signed value (24 bit)
Example: 123456*10-3 = FD01 E240(16)

The data value is a 32-bit number - not two 16-bit numbers. You should be extracting the top 8 bits as the signed exponent and the remaining 24 bits are the mantissa.

Pete
 
I did some tests and this is what I got:

If the load is 4.507W I get 65024 in the first int and 4507 in the second
if the load is 46.61W i get 65024 in the first int and 4616 in the second
if the load is 1938.2W I get 65280 in the first int and 19382 in the second

Can you confirm that for 4.507W you were getting 65024? I was hoping for my theory that value would be 64768
 
What I did is C fun - but solution to the wrong problem given guess from original post.
Funny enough, your solution would have worked only if there was something like an unsigned float type in C.
The format of the data are explained in Appendix A of the manual on page 77.
Data type T5 - Unsigned Measurement (32 bit):
bits # 31...24 Decade Exponent (Signed 8 bit)
bits # 23...00 Binary Signed value (24 bit)
Example: 123456*10-3 = FD01 E240(16)

The data value is a 32-bit number - not two 16-bit numbers. You should be extracting the top 8 bits as the signed exponent and the remaining 24 bits are the mantissa.

Pete
correction: bits # 23...00 Binary Unsigned value (24 bit)

Thanks for clearing up the mystery.
 
There must be an easier way:
Code:
float thevalue(uint16_t highword, uint16_t lowword){
  int8_t expo = highword>>8;
  int32_t mant = ((int32_t)((int16_t)(highword<<8))<<8)+lowword;
  float value=(float)mant*pow(10,expo);
  Serial.println(value,abs(expo));
  return value;
}
 
Can you confirm that for 4.507W you were getting 65024? I was hoping for my theory that value would be 64768

Sorry not right now. I need to re-program the microcontroller but I assembled it inside the analyzer :(
 
The format of the data are explained in Appendix A of the manual on page 77.
Data type T5 - Unsigned Measurement (32 bit):
bits # 31...24 Decade Exponent (Signed 8 bit)
bits # 23...00 Binary Signed value (24 bit)
Example: 123456*10-3 = FD01 E240(16)

The data value is a 32-bit number - not two 16-bit numbers. You should be extracting the top 8 bits as the signed exponent and the remaining 24 bits are the mantissa.

Pete

Currently I am using this method :

Code:
ULone = data[1]/(pow(10,(((uint16_t)(~data[0]+0b01))/256)));

and if
Code:
data[0] = 65024
data[1] = 4616

i get:
Code:
ULone = 46.16

The results are all correct so it seem it is the right approach
 
Code:
ULone = ((data[0]<<8)|data[1])/(pow(10,((uint16_t)(~(data[0]>>8)+0b01))));

Should this be the correct calculation.. I didn't test it

I think that the calculation inmy previous post works because I didn't go over very large numbers and so it doesn't affect anything since the MSB's are always 0
 
no,
Code:
here's from post [URL="https://forum.pjrc.com/threads/58155-Two-bytes-to-floating-point-number?p=219986&viewfull=1#post219986"]https://forum.pjrc.com/threads/58155-Two-bytes-to-floating-point-number?p=219986&viewfull=1#post219986[/URL]
highword 65022	lowword 7616	result -123.456
highword 255	lowword 53191	result -12345
highword 64768	lowword 4507	result 4.507
highword 65024	lowword 4616	result 46.16
highword 65280	lowword 19382	result 1938.2
highword 64769	lowword 57920	result 123.456    //example from manual

your's
highword 65022	lowword 7616	result 76.160
highword 255	lowword 53191	result 0
highword 64768	lowword 4507	result 4.507
highword 65024	lowword 4616	result 46.16
highword 65280	lowword 19382	result 1938.2
highword 64769	lowword 57920	result 579.200
 
How about this:

Code:
ULone = (((data[0]&0xf)<<16)| data[1])/(pow(10,((~(data[0]>>8)&0xf)+0b01)));
 
There must be an easier way:
Code:
float thevalue(uint16_t highword, uint16_t lowword){
  int8_t expo = highword>>8;
  int32_t mant = ((int32_t)((int16_t)(highword<<8))<<8)+lowword;
  float value=(float)mant*pow(10,expo);
  Serial.println(value,abs(expo));
  return value;
}

Except that the mantissa is unsigned, see data type T5 in the manual.
Code:
float thevalue(uint16_t highword, uint16_t lowword){
  int8_t expo = highword>>8;
  uint32_t mant = ((uint32_t)((uint16_t)(highword<<8))<<8)+lowword; //unsigned mantissa
  float value=(float)mant*pow(10,expo);
  Serial.println(value,abs(expo));
  return value;
}
 
Status
Not open for further replies.
Back
Top