Our full technical support staff does not monitor this forum. If you need assistance from a member of our staff, please submit your question from the Ask a Question page.

Log in or register to post/reply in the forum.

Float16 (half Precision)

Curt_Ingram Nov 8, 2021 02:13 AM

I'm trying to read data from a Morningstar Sunsaver MPPT that outputs modbus in Float16, is there anyone that has done this with the modbuusmaster command?  

nsw Nov 8, 2021 10:32 AM

There is an example program on our website for the Morningstar Sunsaver MPPT using a CR1000X. It can be found here:


Curt_Ingram Nov 9, 2021 01:49 AM

unfortunately, I made a mistake.  It is a TriStar MPPT, and they changed all of their output types to float16.  The example provided does work with my sunsavers for sure but no this new controller.  :(

Thanks you for the reply though!


notcabbage Nov 9, 2021 03:23 AM

Here's a (clunky) subroutine I whipped up to convert an Integer16 into a float16. Basically, we're just reading 2 bytes in a modbus register, converting that signed integer16 to binary, and then converting back to a float 16.

Example: Reading the battery voltage register we get back 21226


Public Result, MPPTCharger, battvolts As Long, value As Float

'========FLOAT 16===========
Sub f$float16 (returnValue, int16 As Long)
  Dim binary As String, exponent As Long, i As Long, temp As Long, mantissa As Float
  binary=FormatLong (int16,"%b")
  exponent = -15
  For i = 1 To 5
    temp = Mid(binary,6-i,1)
    exponent += temp*2^(i-1)
  Next i
  exponent = 2^exponent
  mantissa = 0
  For i = 6 To 15
    temp = Mid(binary,21-i,1)
    mantissa += temp*2^(i-6)
  Next i
  mantissa = 1+ mantissa/1024
  If int16 < 0 Then
    mantissa = mantissa*-1
  End If
  returnValue = exponent*mantissa

  Scan (15,Sec,0,0)
    TCPOpen ("",502,1,500,MPPTCharger,1)
    ModbusMaster (Result, MPPTCharger,9600,1,3,battvolts,25,1,3,200,1)
    Call f$float16(value,battvolts)


This returns the expected value of 55.3125. Hopefully a MoveBytes wizard can come around and do something slicker, but this is my solution until ModbusMaster can handle half-precision floats.

Jaab Nov 10, 2021 07:06 PM

Here is a program developed for the Tristar by someone at CSI that we've used in the past: 

Public result
Public modin(82) As Long

Public N_sys_V

Public Charge_Current, Battery_Terminal_Voltage, Array_Current, Array_Voltage
Public Load_Voltage, Load_Current, Battery_Current
Public HeatSink_Temperature, Battery_Temperature
Public Charger_Output_Power, Array_Vmp,Array_Pmax,Array_Voc

Sample (1,Charge_Current,FP2)
Sample (1,Battery_Terminal_Voltage,FP2)
Sample (1,Battery_Current,FP2)
Sample (1,Load_Current,FP2)
Sample (1,HeatSink_Temperature,FP2)
Sample (1,Battery_Temperature,FP2)
Sample (1,Array_Vmp,FP2)
Sample (1,Array_Voc,FP2)
Sample (1,Array_Pmax,FP2)
Sample (1,Charger_Output_Power,FP2)

Function LongToFloat16(SourceLong As Long) As Float   

'Takes a Float16 stored in the lower 2 bytes of a Long, and converts to a Float

Dim SignBit As Long
Dim Exponent As Long
Dim Fraction As Long
Dim tempLong As Long
Dim result As Float 'The Float16 moved into a single precision 32 bit float


SignBit = (SourceLong >> 15) AND 1
Exponent = ((SourceLong >> 10) AND 31) -15 + 127 'Float16 has a bias of 15, Float32 has a bias of 127

If Exponent = 143 Then Exponent = 255 'NAN catch
If Exponent = 112 Then Exponent = 0 'Zero catch
Fraction = SourceLong AND 1023

tempLong = (Exponent << 23) OR (Fraction << 13 )
MoveBytes (result,0,tempLong,0,4)
If SignBit Then result = result * -1 'Since long are signed, I can't just OR the sign bit

Return result

BeginProg  SerialOpen (ComRS232,9600,7,0,50)

Scan (5,Sec,0,0)

'Code to measure Tristar charge controller
ModbusMaster (result,ComRS232,9600,1,3,modin(),1,82,3,100,3)

Charge_Current = LongToFloat16((modin(17)))
Battery_Terminal_Voltage = LongToFloat16((modin(19)))
Array_Current = LongToFloat16((modin(18)))
Array_Voltage = LongToFloat16((modin(20)))
'Battery_Sense_Voltage = LongToFloat16((modin(24)))
Load_Voltage = LongToFloat16((modin(21)))
Battery_Current = LongToFloat16((modin(22)))
Load_Current = LongToFloat16((modin(23)))
HeatSink_Temperature =LongToFloat16((modin(27)))
Battery_Temperature = LongToFloat16((modin(28)))
Charger_Output_Power = LongToFloat16((modin(61)))
Array_Vmp = LongToFloat16((modin(62)))
Array_Pmax = LongToFloat16((modin(63)))
Array_Voc = LongToFloat16((modin(64)))

CallTable power


Curt_Ingram Nov 11, 2021 03:48 AM

Wow! Thanks everyone!  I can't wait to give it a try!


Log in or register to post/reply in the forum.