Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 25 of 25

Thread: Two bytes to floating point number

  1. #1

    Two bytes to floating point number

    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!

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,934
    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.

  3. #3
    Senior Member
    Join Date
    Nov 2017
    Location
    Belgium
    Posts
    136
    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.

  4. #4
    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).

  5. #5
    @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?

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,680
    Does the manufacturer of this power meter publish any guidance on how to use the data they provide? Maybe share that info here?

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

  8. #8
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,934
    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

  9. #9
    @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!

  10. #10
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,934
    Quote Originally Posted by Tady View Post
    @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.

  11. #11
    Senior Member
    Join Date
    Nov 2017
    Location
    Belgium
    Posts
    136
    Quote Originally Posted by Tady View 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
    Quote Originally Posted by Tady View Post
    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.
    Quote Originally Posted by Tady View Post
    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?

  12. #12
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    9,934
    For 2's compliment see : https://forum.arduino.cc/index.php?topic=42601.0 - post #7>#8

    or @el_supremo / Pete posted here for two bytes into a 16 bit signed in :: https://forum.arduino.cc/index.php?topic=367579.0

  13. #13
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,166
    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

  14. #14
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,231
    Quote Originally Posted by Tady View Post
    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

  15. #15
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,166
    Yeah, that one looked wrong to me too.

    Pete

  16. #16
    Senior Member
    Join Date
    Nov 2017
    Location
    Belgium
    Posts
    136
    Quote Originally Posted by defragster View Post
    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.
    Quote Originally Posted by el_supremo View Post
    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.

  17. #17
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,166
    Ooopps. Yup, copy and paste error.

    Pete

  18. #18
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    133
    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;
    }

  19. #19
    Quote Originally Posted by manitou View Post
    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

  20. #20
    Quote Originally Posted by el_supremo View Post
    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

  21. #21
    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

  22. #22
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    133
    no,
    Code:
    here's from post https://forum.pjrc.com/threads/58155...l=1#post219986
    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

  23. #23
    How about this:

    Code:
    ULone = (((data[0]&0xf)<<16)| data[1])/(pow(10,((~(data[0]>>8)&0xf)+0b01)));

  24. #24
    Senior Member
    Join Date
    Nov 2017
    Location
    Belgium
    Posts
    136
    Quote Originally Posted by bicycleguy View Post
    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;
    }

  25. #25
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    133
    Quote Originally Posted by neurofun View Post
    Except that the mantissa is unsigned, see data type T5 in the manual.
    Yah, mine would be a T6

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •