Page 1 of 1
FLOAT datatype
Posted: Wed May 18, 2022 2:19 pm
by kp3928
I try to decode the temperature value of the TG-BT5-OUT bluetooth tag.
Hex value is (signed) 0x19a1
Real temperature is (0x19a1)/256 = 25.6 degrees Celcius.
I want to push the real temperature directly into an MQTT message body.
As far as I can see there is only TONUM() available for this type of operations, but that is integer only, so that will report 26 degrees.
Any suggestion or trick to mimic a TOFLOAT() function ?
Re: FLOAT datatype
Posted: Wed May 18, 2022 3:17 pm
by rextended
Hmmm.....
....
See the new function than consider also the negative values:
viewtopic.php?p=934123#p934123
Re: FLOAT datatype : TG-BT5-OUT decode
Posted: Wed May 18, 2022 6:15 pm
by kp3928
Wow, impressive trick, I will try tonight and let you know.
Re: FLOAT datatype
Posted: Thu May 19, 2022 10:26 am
by kp3928
Thank you, that works fine indeed, will adapt for temps below 0 and share here.
At least it allows me to push the temp data of the TG-BT5-OUT to MQTT without any client hassle.
Re: FLOAT datatype [SOLVED]
Posted: Thu May 19, 2022 2:05 pm
by rextended
This function is for you, the 2nd parameter is the wanted decimals (obviously from 0 to 3):
This is for float 8bit.8bit
Expected input value are on big-endian ("0x" . #15 . #14)
The input values can be any between 0x0 and 0xffff
test for positive temperature:(0x19a1)/256 = 25.628 C
test for negative temperature: (0xe65f)/256 = -25.628 C
:global float2deg do={
:local float [:tonum $1]
:local decimalsign "," ; # in Italy it is "," in other countries can be "."
:local sign "" ; # if wanted can be "+"
:if ($float > 32767) do={:set float (($float - 65536) * -1); :set sign "-"}
:local forthousand ($float * 1000)
:local forthousand ($forthousand / 256) ; # MikroTik offset
:local ftstring "00$[:tostr $forthousand]"
:local pickpos ([:len $ftstring] - 3)
:local decimals [:pick $ftstring $pickpos ($pickpos + [:tonum $2])]
:local celsius ($forthousand/1000)
:if ([:tonum $2] > 0) do={:set celsius "$celsius$decimalsign$decimals" }
:return "$sign$celsius"
}
terminal code
[rex@tended] > :global float2deg do={
{... :local float [:tonum $1]
{... :local decimalsign "," ; # in Italy it is "," in other countries can be "."
{... :local sign "" ; # if wanted can be "+"
{... :if ($float > 32767) do={:set float (($float - 65536) * -1); :set sign "-"}
{... :local forthousand ($float * 1000)
{... :local forthousand ($forthousand / 256) ; # MikroTik offset
{... :local ftstring "00$[:tostr $forthousand]"
{... :local pickpos ([:len $ftstring] - 3)
{... :local decimals [:pick $ftstring $pickpos ($pickpos + [:tonum $2])]
{... :local celsius ($forthousand/1000)
{... :if ([:tonum $2] > 0) do={:set celsius "$celsius$decimalsign$decimals" }
{... :return "$sign$celsius"
{... }
[rex@tended] > :put [$float2deg 0x19a1 0]
25
[rex@tended] > :put [$float2deg 0x19a1 1]
25,6
[rex@tended] > :put [$float2deg 0x19a1 2]
25,62
[rex@tended] > :put [$float2deg 0x19a1 3]
25,628
[rex@tended] > :put [$float2deg 0xe65f 0]
-25
[rex@tended] > :put [$float2deg 0xe65f 1]
-25,6
[rex@tended] > :put [$float2deg 0xe65f 2]
-25,628
[rex@tended] > :put [$float2deg 0xe65f 3]
-25,628
Re: FLOAT datatype
Posted: Thu May 19, 2022 4:57 pm
by Larsa
@Rextended, the wizard of scripts! Nice work, it's a keeper!
Re: FLOAT datatype
Posted: Sat May 21, 2022 7:52 pm
by kp3928
@rextended blazing fast, thank you, I did about the same but you were first!
I should visit the forum more often
Re: FLOAT datatype
Posted: Wed Jul 13, 2022 11:08 am
by germarsh
Thank you for this. I came across a similar problem in attempting to send a floating value via MQTT from the LTE1 interface.
This is my "noddy" solution:
:if ($rsrqLen > 2) do {
:set $intpart ($rsrq/10)
:set $decpart ($rsrq%10*-1)
:set $rsrqvalue ($intpart . "." . $decpart)
} else={ :set $rsrqvalue $rsrq}
Re: FLOAT datatype
Posted: Wed Jul 13, 2022 12:21 pm
by rextended
Thanks for sharing, but is "another" problem.
On OP case, is a HEX Float value to be converted on decimal (string)
On your case, you have a integer, than rapresent 10 times the value???
and you want convert it on float (string)???
Is not clear how to use that fragment, because is out of the contest where is used.
Re: FLOAT datatype
Posted: Fri Mar 31, 2023 2:35 pm
by kreload
Any script for 32 bit float that will work with sensors where the value is based on 2 word length?
Ex
{:local output [/iot modbus read-holding-registers slave-id=0x21 num-regs=0x2 reg-addr=0x1000 as-value once];:put [($output->"values")]}
80500;31200
Re: FLOAT datatype
Posted: Fri Mar 31, 2023 5:21 pm
by rextended
I do not understand what you mean, give example on single value and corresponding true value.
2 word = 2 distinct DWORD on one array?
80500 = ?
31200 = ?
Re: FLOAT datatype
Posted: Fri Mar 31, 2023 5:25 pm
by Amm0
You can get to the modbus float to string with a script I suspect. But you do run into the problem you can NOT compare or do math on them, unless you convert to integer - since there are no "real" floats.
So what your hoping to do with these floats becomes important
Re: FLOAT datatype
Posted: Sat Apr 01, 2023 12:01 am
by Amm0
Any script for 32 bit float that will work with sensors where the value is based on 2 word length?
You'd also have to know the byte-order, e.g. big endian or little endian, to convert to anything.
Re: FLOAT datatype
Posted: Mon Apr 03, 2023 4:50 pm
by kreload
I do not understand what you mean, give example on single value and corresponding true value.
2 word = 2 distinct DWORD on one array?
80500 = ?
31200 = ?
I'm new to modbus stuff so forgive me if i can't explant very good. I also writed wrong 80500 on the first result.
I have multiple energy meters where every value is stored according the the manufactures on 2 registers if i use IEEE values table and starting from 0x1000 or 4 registers starting from 0x0000.
So, to read System Voltage i interogate 0x1000 (or 44097) with a length of 2 (or 4 for the alternative table). I used length of 2 because i thought it will be simplier.
With Mikrotik i get this result: 18630;49152 . These values, in hex, are actually 0x48C6;0xC000
If you convert 0x48C6C000 on a IEEE 754 converter to decimal you get 402,944 which are mW according to the manual or 402.9 V.
So FFS, how do i transform 18630;49152 to 402944 directly on Mikrotik?
Re: FLOAT datatype
Posted: Tue Apr 04, 2023 12:24 am
by rextended
0x48C6C000 from IEEE 754 to decimal is 407040, probably is 407,04 mW (why V ???)
402944 is 0x48c
4c000 and probably is 402,944 mW???
So FFS, how do i transform 18630;49152 to 402944 directly on Mikrotik?
Step 1, convert the value from array of integer to one hex value.
{
:local output {18630;49152} ; # simulate the reading
# a = 18630 = 0x48C6
# b = 49152 = 0xC000
# (a * 0x1000) + b = (0x48C6 * 0x10000) + 0xC000 = 0x48C60000 + 0xC0000 = 0x48C6C000 = 1220984832
:local fullvalue ((($output->0) * 0x10000) + ($output->1))
:put $fullvalue
}
output code
1220984832
Re: FLOAT datatype
Posted: Tue Apr 04, 2023 12:24 am
by rextended
search tag # rextended ieee754toint IEEE754 DWORD FLOAT to number
Functions to convert DWORD FLOAT as IEEE754 to number
This feature is not complete.
RouterOS does not support numbers larger than 9223372036854775807 or smaller than -9223372036854775808, and it also does not support decimal division.
So we can only have an approximation, because the float type go from
(+/-)0,(put 45 zeros here)140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125
to
(+/-)340282346638528859811704183484516925440
and
is impossible to represent correctly on RouterOS.
So this function, in extreme cases, could produce an incorrect value.
:global ieee754toint do={
:local input [:tonum "$1"]
:local hack 0x3B9ACA00 ; # Hack, RouterOS do not support decimal numbers...
:local isneg ($input >> 31)
:local exponent (($input >> 23) & 0xFF)
:local powerof2 1
:for x from=1 to=($exponent - 0x7F) step=1 do={:set powerof2 ($powerof2 * 2)}
:set exponent $powerof2
:local mantissa ($input & 0x7FFFFF)
:set powerof2 $hack
:local temp $hack
:for x from=22 to=5 step=-1 do={ ; # is 5 and not 0 because missing support for decimals on RouterOS
:set powerof2 ($powerof2 / 2)
:if ((($mantissa >> $x) & 1) = 1) do={
:set temp ($temp + $powerof2)
}
}
:set mantissa $temp
:local total (($exponent * $mantissa) / $hack)
:local decimal (($exponent * $mantissa) % $hack)
:if ($decimal > 444444444) do={:set total ($total +1)}
:if ($isneg = 1) do={:set total ($total * -1)}
:return $total
}
example code
{
:local output {18630;49152} ; # simulate the read
# a = 18630 = 0x48C6
# b = 49152 = 0xC000
# (a * 0x1000) + b = (0x48C6 * 0x10000) + 0xC000 = 0x48C60000 + 0xC0000 = 0x48C6C000 = 1220984832
:local fullvalue ((($output->0) * 0x10000) + ($output->1))
:set fullvalue [$ieee754toint $fullvalue]
:local intmw ($fullvalue / 1000)
:local decmw "00$($fullvalue % 1000)" ; :set decmw [:pick $decmw ([:len $decmw] - 3) [:len $decmw]]
:put "$intmw,$"decmw"mW"
}
result code
407,040mW
next convert 407040,0 to it's intended value:
407040,0 / 1000 = 407,040 mW