News:

Let's find out together what makes a PIC Tick!

Main Menu

Converting byte array to float

Started by jpeeters19, Apr 27, 2021, 10:56 AM

Previous topic - Next topic

jpeeters19

I am having trouble to convert a byte array which holds a float value to a real float variable. What is the easiest way to do this?

rox

Hi, I dont have issue using Les example on IEEE-754 floating point conversion, if your not getting the correct value after conversion, maybe its due to endianess, try byte swap or word swap or byte swap and word swap before doing the conversion.

Simon

I think it would help if you said how you are storing your float as a byte array, there are different ways of doing that.

jpeeters19

I have a long string with sensordata coming wireless from my weather station which is then loaded into a byte array:
+RCV=50001,140,WST1:1.745,1.845,35,60,0.01,16.46,31.97,101033.92,17.25,37.58,17.0,00-00-00 00:00:00,0,00,000000,0,9.97,0.15,0,13.79,0.00,0,14.42,0
.04,#EOF#,-53,45

i.e.
1.745 is stored as
bytearray[1] = 1
bytearray[2] = .
bytearray[3] = 7
bytearray[4] = 4
bytearray[5] = 5

I have some code to extract that value to a float variable, but i think it should be easier?
code i have:


TempString = ByteArray[counter1 + 1]
PM1High = Val(TempString,Dec)

Clear TempString
TempString = ByteArray[counter1 + 3] + ByteArray[counter1 + 4] + ByteArray[counter1 + 5]
PM1Low = Val(TempString,Dec) ' Convert the String into an integer
                           
PM1Low = PM1Low / 1000
PM1Float = PM1High + PM1Low
SerOut NextionRX, 84, ["pm1float:", Dec PM1Float, 13, 10]

John Drew

#4
If pmwlow is an integer then dividing it by 1000 will always give an answer of 0 or 1. You need to assign to a float earlier e.g  using fpmflow as a float do:
fpmlow = pmwlow
Then fpmlow = fpmlow/1000
Next you add the whole number.
You don't need to convert the whole number part to a float as that will happen automatically as you are assigning the calculation to a float.
John

jpeeters19

i forgot to mention, pm1low is declared as float in the beginning of my programm

Dim PM1Float As Float
Dim PM1High As Word
Dim PM1Low As Float

the code works, just tested it, but i was wondering can it be easier done?

John Drew

Understood.
I'll have a think about it. I've used GPS strings often.
On my phone now, will check my computer tomorrow.
John

John Drew

#7
Hi jpeeters19
I've attached the code I use to extract data from a GPS string. I hope it's helpful. It works by counting commas. There's a bit of test code in there to use with ISIS so that needs to be removed and there's a couple of commented lines that should be removed too.
John

'****************************************************************
'*  Name    : GPScommas.BAS                                     *
'*  Author  : John Drew VK5DJ                                   *
'*  Notice  : Copyright (c) 2018 [select VIEW...EDITOR OPTIONS] *
'*          : All Rights Reserved                               *
'*  Date    : 06/05/2018                                        *
'*  Version : 1.0                                               *
'*  Notes   : To demonstrate how to read a GPS NMEA string      *
'*          : using the count commas approach                   *
'*          : This is more reliable than just anticipating the  *
'*          : position of the items                             *
'****************************************************************
'
' Read and display GPS info on a 16*2 LCD.
' LCD ports set to enable exploration using the demo ISIS program
' To use in real life uncomment the 3 HRSIN lines and comment out/remove the sample data
'
;-------------------------------------------------------------------------------
;**** Added by Fuse Configurator ****
; Use the Fuse Configurator plug-in to change these settings

Device = 16F628A

Declare Reminders Off
@ CONFIG_REQ = 0 ; Override Compiler's configuration settings
Asm-
__Config  0x3F6B ;FOSC_ECIO & WDTE_OFF & PWRTE_OFF & MCLRE_ON & BOREN_ON & LVP_OFF & CPD_OFF & CP_OFF
Endasm-
Declare Reminders On
Declare Optimiser_Level = 3

;**** End of Fuse Configurator Settings ****
;-------------------------------------------------------------------------------
Declare Xtal = 10

'set up the LCD
Declare LCD_Type 0     'text type
Declare LCD_DTPin PORTB.4 'assigns data lines to B4..7 
Declare LCD_ENPin PORTB.0     'enable pin set for ISIS
Declare LCD_RSPin PORTB.3     'RS line pin set for ISIS
Declare LCD_Interface 4   '4 or 8 line interface
Declare LCD_Lines 2 'lines in the display
Declare LCD_DataUs 255                  'delay data to allow for slow LCD
Declare LCD_CommandUs 5000              'add a delay for commands, normally 2000

'set up the serial port
Declare Hserial_Baud = 9600        ' Set baud rate to 9600
Declare Hserial_RCSTA = %10010000  ' Enable serial port and continuous receive
Declare Hserial_TXSTA = %00000000       ' TX disabled
Declare Hserial_Clear = On              ' Optionally clear the buffer before receiving

Symbol LockLed = PORTA.2
Symbol GPSfix  = PORTA.1

TRISB=%00001010   ' (outputs) RB0, RB2, RB4-7
                          ' (inputs) RB1, RB3
TRISA=%11100001                ' Outputs RA1-4, Inputs RA0, RA5-7

'----[VARIABLE DECLARATIONS]-------------------------------------------------

Dim Count1            As Byte       'following used in find commas
Dim Count2            As Byte
Dim bTemp             As Byte
Dim I                 As Byte       'loop variable
Dim CommaNumber       As Byte       'item number used in comma search
Dim Quality           As Byte
Dim Fix               As Byte

'input and item arrays
Dim RX_BUFF[68]       As Byte       'receiver buffer 70 bytes beginning at number zero
Dim ValueArray[12]    As Byte
Dim TimeArray[12]     As Byte
Dim DateArray[12]     As Byte
Dim LatArray[12]      As Byte
Dim LongArray[12]     As Byte
Dim SatArray[12]      As Byte
Dim AltArray[12]      As Byte
Dim QualityArray[12]  As Byte
Dim FixArray[12]      As Byte

'Messages in EEDATA for use in formatting
Time       EData  "Time : ",0
Date       EData  "Date : ",0
Lat        EData  "Lat  : ",0
Lon        EData  "Lon  :",0
Qual       EData  "Quality: ",0
QualRange  EData  " (0-9)   ",0   
FixType    EData  "Fixtype: ",0
TwoDfix    EData  "2D fix",0
ThreeDfix  EData  "3D fix",0
Sats       EData  "Sats : ",0
Blanks     EData  "        ",0
Alt        EData  "Alt  : ",0
Metres     EData  " m",0
Waiting    EData  "Waiting for fix ",0

'-----------------------------------------------------------------------------

DelayMS 200                                   'Wait for the PIC® micro to stabilise
All_Digital = TRUE                            'Set PORTA and PORTE to all digital

GoTo MAIN                                     'Jump over the subroutines

FindItem:
        For I = 0 To 11                       'clear values
            ValueArray[I]=0                   'and effectively create a string by ensuring a zero is left
        Next                                  'at the end after data is read in
        If CommaNumber > 0 Then               'the position of the required data
          Count1 = 0: Count2 = 0              'set loop variables
          Repeat                              'find the appropriate comma as set in commanumber
             bTemp = RX_BUFF[Count2]
             If bTemp = "," Then Inc Count1
             Inc Count2       
          Until Count1 = CommaNumber          'found position of the wanted comma in Count2
          Count1=0                            'now read in data from the wanted comma
          If RX_BUFF[Count2]="," Then         'if a blank entry is found
             ValueArray="N/A"
             Return
          EndIf
        EndIf 
         
        Repeat                                'now load the array for printing
            ValueArray[Count1] = RX_BUFF[Count2]
            Inc Count1: Inc Count2
        Until RX_BUFF[Count2] = ","           'ValueArray now has the chosen item
Return                                        'and ready for data to be copied to wanted string
 

MAIN:
    Cls
    'HRSIn Wait("$GPGGA,"),Str RX_BUFF\63     'Pull the GPGGA string from the GPS data
    RX_BUFF="044707.00,3307.33219,S,15123.27808,E,1,12,1.24,245.9,M,19.0,M,,*7C"          'test data
   
    For CommaNumber = 0 To 8
      GoSub FindItem
      Select CommaNumber                      'now access the GGA string
      Case 0
           TimeArray = ValueArray             'extract time at first position
      Case 1
           LatArray = ValueArray              'extract latitude at 1st comma
      Case 3
           LongArray = ValueArray             'extract longitude at 3rd comma
      Case 5
           QualityArray = ValueArray          'extract quality 0-9 where 0=invalid, 1=GPS fix etc   
      Case 6
           SatArray = ValueArray              'how many satellites at 6th comma
      Case 8
           AltArray = ValueArray              'get altitude/height at 8th comma
      End Select     
   Next                                       'normal operating flow drops into Checkfix
 Checkfix:                                    'check GPGSA string for fix
'   HRSIn Wait("$GPGSA,"),Str RX_BUFF\10      'Get the GPGSA string from the GPS data, only need second item
   RX_BUFF ="A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39"     'test data remove after test in ISIS
   
   CommaNumber = 1                            'extract the satellite 3D fix 1=no fix,2=2D fix, 3=3D fix
   GoSub FindItem
   FixArray = ValueArray
   Fix = fixarray#0 - 48                      '3D fix as decimal to use for messages
   If Fix < 2 Then                            'no fix so can't display data
      Print At 1,1,EStr Waiting               'put waiting message on screen
      GoTo Checkfix
   EndIf         
   
'   HRSIn Wait("$GPRMC,"),Str RX_BUFF\63      'Pull the GPRMC string from the GPS data
   RX_BUFF ="044709.00,A,3307.33219,S,15123.27808,E,0.136,,160117,,,A*6B"  'test data remove after test in ISIS
   
   CommaNumber = 8                            'extract date after 8th comma
   GoSub FindItem
   DateArray = ValueArray
   

   'now print the values
   'time and date with formatting
   Print At 1,1, EStr Time, TimeArray[0], TimeArray[1],":",TimeArray[2],TimeArray[3],":",TimeArray[4],TimeArray[5],EStr Blanks
   Print At 2,1, EStr Date, DateArray[0],DateArray[1],"/",DateArray[2],DateArray[3],"/",DateArray[4],DateArray[5],EStr Blanks
   DelayMS 3000
   
   'latitude and longitude with no formatting so structure deg+deg+min+min+decimal mins
   Print At 1,1,EStr Lat, Str LatArray
   Print At 2,1,EStr Lon, Str LongArray
   DelayMS 3000
   
   Print At 1,1,EStr Sats, Str SatArray, EStr Blanks
   Print At 2,1,EStr Alt , Str AltArray, EStr Metres, EStr Blanks     
   DelayMS 3000
   
   Print At 1,1, EStr Qual, Str QualityArray, EStr QualRange
   Print At 2,1, EStr FixType
   If Fix = 2 Then
      Print EStr TwoDfix, EStr Blanks
   Else
      Print EStr ThreeDfix, EStr Blanks
   EndIf     
   DelayMS 3000

End

david

Hello John,
I may be wrong here but your excellent GPS example shows how to capture and display a floating point string but I think the original poster is questioning if there is a more elegant way of capturing the serial string and converting it to a numeric float as well as for display.
He is extracting the integer part then taking the 3 decimal point values as an integer and dividing by 1000 to get a fractional part, then adding the two parts together.  He has a solution but questions if there's a better method.
Using your GPS example it would be like taking the floating point speed in knots from the RMC sentence as a numeric value for say conversion to another unit like m/s.
It was a strange coincidence that on the same day I was doing much the same and also thought - that's a bit clunky- there has to be a nicer way to do this but maybe there isn't.
At least the OP's example appears to be a fixed length whereas the RMC knots could be n.nnn to nnn.nnn so you first need to locate the decimal point.

Best regards,
David 

tumbleweed

Another thing you have to keep in mind when working with 32-bit floats is that the format is only good for about 6-7 decimal digits.

Floating-point gives you a large range of numbers, but not a lot of digits.

jpeeters19

Thanks for the nice example John but i am only looking for a simpler way to convert the serial string into a numeric float as David mentioned.
Sometimes the lower part of my float values (dec2) change, i.e.:
312.23
72.23
1.23

so my program gets even longer..
 
(part of my code):

If Commapointer = 2 Then  'PM1.0 float value
                            'tel aantal decimalen
                             For dummycount = 1 To 9
                                If ByteArray[counter1 + dummycount] = "." Then Break
                             Next dummycount
                                                   
                               If dummycount = 7 Then '2.333
                                    Clear TempString                       
                                    TempString = ByteArray[counter1 + 6]
                                    PM1High = Val(TempString,Dec)
                                   
                                    Clear TempString
                                    TempString = ByteArray[counter1 + 8] + ByteArray[counter1 + 9] + ByteArray[counter1 + 10]
                                    PM1Low = Val(TempString,Dec) ' Convert the String into an integer
                                                                               
                                    PM1Low = PM1Low / 1000
                                    PM1Float = PM1High + PM1Low
                                    SerOut NextionRX, 84, ["pm1float:", Dec PM1Float, 13, 10]
                                                       
                                    GoTo ZoekVolgendeComma
                               EndIf
                           
                               If dummycount = 8 Then '12.333
                                    Clear TempString                       
                                    TempString = ByteArray[counter1 + 6] + ByteArray[counter1 + 7]
                                    PM1High = Val(TempString,Dec) ' Convert the String into an integer
                                   
                                    Clear TempString
                                    TempString = ByteArray[counter1 + 9] + ByteArray[counter1 + 10] + ByteArray[counter1 + 11]
                                    PM1Low = Val(TempString,Dec) ' Convert the String into an integer
                                                                               
                                    PM1Low = PM1Low / 1000
                                    PM1Float = PM1High + PM1Low
                                    SerOut NextionRX, 84, ["pm1float:", Dec PM1Float, 13, 10]
                                    GoTo ZoekVolgendeComma
                               EndIf
                               
                               If dummycount = 9 Then '212.333
                                    Clear TempString                       
                                    TempString = ByteArray[counter1 + 6] + ByteArray[counter1 + 7] + ByteArray[counter1 + 8]
                                    PM1High = Val(TempString,Dec) ' Convert the String into an integer
                                   
                                    Clear TempString
                                    TempString = ByteArray[counter1 + 10] + ByteArray[counter1 + 11] + ByteArray[counter1 + 12]
                                    PM1Low = Val(TempString,Dec) ' Convert the String into an integer
                                                                               
                                    PM1Low = PM1Low / 1000
                                    PM1Float = PM1High + PM1Low
                                    SerOut NextionRX, 84, ["pm1float:", Dec PM1Float, 13, 10]
                                    GoTo ZoekVolgendeComma
                               EndIf
                      EndIf
               

top204

#11
Include "Strings.inc"        ' Load the extra String Handling library into the program

Dim MyString As String * 20 = "3.14"
Dim MyFloat As Float

MyFloat = StrToFloat(MyString)


To use the StrToFloat procedure with a byte array, it must be null terminated, so it looks like a String variable to the procedure's code:

Include "Strings.inc"        ' Load the extra String Handling library into the program
   
Dim MyArray[20] As Byte
   
StrN MyArray = "3.14"          ' Load the array with a null terminated character string
MyFloat = StrToFloat(MyArray)  ' Convert it

david

Thanks for the example code.  That's something I was completely unaware of.
It's the sort of command I was hoping to find but I don't think it's mentioned in the 8 bit compiler help manual.
A quick example similar to the one given just swallowed a further 950 bytes in my test.   As we are always warned - use floats sparingly.

Cheers,
David

top204

#13
Floating Point is flash memory hungry on an 8-bit device because of its complexity, even with the optimised Asm code I create. However, if Floating Point is used multiple times in a program, the same compiler library subroutines are called, so the flash memory increase gets smaller because the compiler's, standard,  library subroutines are already in place. i.e. multiplication, subtraction, addition and division etc... If the Floating Point trigonometry functions are used, they will increase the flash memory quite a bit. However, with the 128K flash devices, a few extra K bytes used does not actually matter as much. :-) As with the Positron board's PIC18F27K42 device with 128K of flash and 8K of RAM and 1K of "real" EEPROM, operating at 64MHz. A really nice device. :-)

All the compilers external library files are located here:

C:\Users\User Name\PDS\Includes\

They are installed with the compiler's installer. The problem with adding them to the compiler manuals is that there are so many, and are constantly being added too, so it would take a few hundred pages just to cover them. :-)

For faster and smaller, and more accurate floating point, use the 16-bit devices. They are super fast and have lovely assembler mnemonics to make things even smaller and faster. i.e. Barrel Shifting in a single clock cycle, hardware multiplication and division etc... I even added 64-bit Floating Point to the 16-bit compiler for a lot more accuracy. :-)

david

Most interesting.  I can see the compiler has more capability than I realized and perhaps it's time I got some newer devices to make use of some of these features. 
Your explanation is much appreciated.

Best regards,
David

jpeeters19

Thanks! didn't know the existence of that function

ranox

Colleagues,

Les has put such an effort into these "includes".

Has anyone prepared an index or contents list (eg spread-sheet) to the various routines and their use ?

PS. that PIC18F27K42 appears awesome !