User Tools

Site Tools


modbus_rtu_custom

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Last revisionBoth sides next revision
modbus_rtu_custom [2019/01/09 12:18] – created emozolyakmodbus_rtu_custom [2022/10/21 12:01] – Float mode for 32 bit (DW) read and write atolstov
Line 9: Line 9:
   *Validation error message: Invalid register address. Valid ModBus addresses are Cxxx, DIxxx, IRxxx, HRxxx.   *Validation error message: Invalid register address. Valid ModBus addresses are Cxxx, DIxxx, IRxxx, HRxxx.
 Code: Code:
-<code lua>+<code lua ModBus RTU custom protocol.lua>
 -- MODBUS RTU Demo Driver -- MODBUS RTU Demo Driver
  
 function createDevices () function createDevices ()
                                                      -- read FC write FC                                                      -- read FC write FC
-  addDevice({name = "C",  shift = 0, base = 10, xtraFields = {1, 5}}) +    addDevice({name = "C",      shift = 0, base = 10, xtraFields = {1, 5}}) 
-  addDevice({name = "DI", shift = 0, base = 10, xtraFields = {2, 0}}) +    addDevice({name = "DI",     shift = 0, base = 10, xtraFields = {2, 0}}) 
-  addDevice({name = "HR", shift = 0, base = 10, xtraFields = {3, 6}}) +    addDevice({name = "HR",     shift = 0, base = 10, xtraFields = {3, 6}}) 
-  addDevice({name = "IR", shift = 0, base = 10, xtraFields = {4, 0}}) +    addDevice({name = "HRI",    shift = 0, base = 10, xtraFields = {3, 6, 1, 0}})   -- uint 0 inverse  
-  +    addDevice({name = "HRIF",   shift = 0, base = 10, xtraFields = {3, 6, 1, 5}})   -- float inverse (maybe 7 ? https://docs.webhmi.com.ua/access_via_api?s[]=float) 
 +    addDevice({name = "HRF",    shift = 0, base = 10, xtraFields = {3, 6, 0, 5}})   -- float 5 
 +    addDevice({name = "IR",     shift = 0, base = 10, xtraFields = {4, 0}}) 
 + 
 end end
 + 
 local errorCount = 0 local errorCount = 0
 + 
 +SLAVE_ADDR    = 1
 +FUNC_CODE     = 2
 +REG_ADDR_HI   = 3
 +REG_ADDR_LO   = 4
 +DATA_LEN_HI   = 5;
 +DATA_LEN_LO   = 6
 +CRC_POS_LO    = 7 -- CRC LITTLE-ENDIAN (lowest comes first!)
 +CRC_POS_HI    = 8
  
 +CRC_BIG_ENDIAN      = false
 +if CRC_BIG_ENDIAN then
 +    CRC_POS_HI      = 7
 +    CRC_POS_LO      = 8 -- CRC BIG-ENDIAN (biggest comes first)
 +end
 -- template  -- template 
-local request = {12     -- slaveId FC +local request = {SLAVE_ADDRFUNC_CODE     
-                 3,  4 -- addr high lo  +                 REG_ADDR_HI,  REG_ADDR_LO   
-                 5,  6-- count hi lo  +                 DATA_LEN_HI,  DATA_LEN_LO   
-                 0,  0      -- crc high lo +                 CRC_POS_LO,  CRC_POS_HI
                 }                      }     
- +  
-EXCEPTIONS = {"Illegal Function",     "Illegal Data Address", +EXCEPTIONS = {  "Illegal Function",         "Illegal Data Address", 
-               "Illegal Data Value",  "Slave Device Failure", +                "Illegal Data Value",       "Slave Device Failure", 
-               "Acknowledge",         "Slave Device Busy", +                "Acknowledge",              "Slave Device Busy", 
-               "Negative Acknowledge", "Memory Parity Error", +                "Negative Acknowledge",     "Memory Parity Error", 
-               "Gateway Path Unavailable", "Gateway Target Device Failed to Respond"+                "Gateway Path Unavailable", "Gateway Target Device Failed to Respond"
               }               }
  
 +-- dataType – The type of data that the user specified for the register. 0 = Bit, 1 = Byte, 2 = Word, 3 = Double Word, 4 = UnixTime
 +DATATYPE    = {DW = 3}
 +DATALEN     = {DW = 4, WORD=2, BYTE=1,BIT=1}
 +FORMAT      = {UINT = 0, FLOAT_32=5}
 +
 +table.unpack = unpack
 function readRegister (reg, device, unitId) function readRegister (reg, device, unitId)
-                             --- FORMING REQUEST -----------  +--------------------------------------------- FORMING REQUEST ---------------------------------------------- 
-      -- slave address +  request[SLAVE_ADDR] = unitId 
-  request[1] = unitId;+ 
 +  request[FUNC_CODE] = device.xtraFields[1] 
 + 
 + 
 +  request[REG_ADDR_HI] = GetHiByte(reg.internalAddr) 
 +  request[REG_ADDR_LO] = GetLoByte(reg.internalAddr) 
 + 
 +  count = reg.dataType == DATATYPE.DW and 2 or 1
      
-  -- function code +  request[DATA_LEN_HI] = GetHiByte(count) 
-  request[2] = device.xtraFields[1] +  request[DATA_LEN_LO] = GetLoByte(count) 
-   + 
-  -- address of register +
-  request[3] = GetHiByte(reg.internalAddr) +
-  request[4] = GetLoByte(reg.internalAddr) +
-   +
-  -- count of registers +
-  local count = 1 +
-      if (reg.dataType == 3then -- double word +
-        count = 2 +
-      end +
-  request[5] = GetHiByte(count) +
-  request[6] = GetLoByte(count) +
-   +
-   -- CRC+
   local crc = GetCRC(request, 2)   local crc = GetCRC(request, 2)
-  local crcLo,crcHi = 0,0 -- will be used below too  +  local crcLo, crcHi -- will be used below too 
-  crcLo = GetLoByte(crc) ; request[7] = crcLo +  crcLo = GetLoByte(crc) ; request[CRC_POS_LO] = crcLo 
-  crcHi = GetHiByte(crc) ; request[8] = crcHi+  crcHi = GetHiByte(crc) ; request[CRC_POS_HI] = crcHi
  
-                           -- SENDING REQUEST +---------------------------------------------- SENDING REQUEST ----------------------------------------------
   if not (sendBytes(request)) then   if not (sendBytes(request)) then
       DEBUG("Can't send bytes")       DEBUG("Can't send bytes")
       return false       return false
   end   end
-                           -- RECEIVING REPLY --- + 
 +---------------------------------------------- RECEIVING REPLY ----------------------------------------------
   local respHead, respData, respCRC = {}, {}, {}   local respHead, respData, respCRC = {}, {}, {}
-   + 
-    -- read Header with length +    -- read Header with length
   respHead = readBytes(3)   respHead = readBytes(3)
   if (respHead == false) then   if (respHead == false) then
Line 77: Line 96:
       return false       return false
   end   end
-   + 
-  if (respHead[1] ~= request[1]) then+  if (respHead[SLAVE_ADDR] ~= request[SLAVE_ADDR]) then
       ERROR("Wrong slaveID in response!")       ERROR("Wrong slaveID in response!")
       return false       return false
   end   end
-   + 
-  if (respHead[2] ~= request[2]) then +    if (respHead[FUNC_CODE] ~= request[FUNC_CODE]) then 
-      if (respHead[2] >= 0x81) then  +        if (respHead[FUNC_CODE] >= 0x81) then 
-          ERROR("EXCEPTION: "..EXCEPTIONS[bit.band(respHead[2], 0x0F)]) +          ERROR("EXCEPTION: "..EXCEPTIONS[bit.band(respHead[FUNC_CODE], 0x0F)]) 
-          readBytes(2) -- read till the end  +          readBytes(2) -- read till the end 
-      else        +        else 
-          ERROR("Wrong Func Code in response"+          ERROR("Wrong Func Code in response. In response = 0d.. tostring( respHead[FUNC_CODE]) .. ', but expected = 0d' .. tostring(request[FUNC_CODE])
-      end  +        end 
-   return false; +        return false; 
-  end +    end 
-   + 
-  local resp_Lentgh = respHead[3]; +    local resp_Lentgh = respHead[3];
         respData = readBytes(resp_Lentgh)         respData = readBytes(resp_Lentgh)
-  if (respData == false) then+    if (respData == false) then
       DEBUG("Can't read response");       DEBUG("Can't read response");
       return false;       return false;
-  end +    end 
-  -- check CRC in reply  +    local tmpResponseTab = {} 
-  local tmpResponseTab = {} +    for i,v in ipairs(respHead) do 
-    for i,v in ipairs(respHead) do  +        table.insert(tmpResponseTab, v) 
-        table.insert(tmpResponseTab, v)  +    end 
-    end  +    for i,v in ipairs(respData) do 
-    for i,v in ipairs(respData) do  +        table.insert(tmpResponseTab, v) 
-        table.insert(tmpResponseTab, v)  +    end 
-    end  + 
-   +    -- check CRC in reply 
-  crc = GetCRC(tmpResponseTab, 0) +    crc = GetCRC(tmpResponseTab, 0) 
-  crcLo = GetLoByte(crc) +    crcLo = GetLoByte(crc) 
-  crcHi = GetHiByte(crc) +    crcHi = GetHiByte(crc) 
-     + 
-  respCRC = readBytes(2) +    respCRC = readBytes(2) 
-  if (respCRC == false) then +    if (respCRC == false) then 
-      DEBUG("Can't read response"); +        DEBUG("Can't read response"); 
-      return false; +        return false; 
-  end +    end 
-  if (respCRC[1] ~= crcLo) or (respCRC[2] ~= crcHi) then  +    if (respCRC[1] ~= crcLo) or (respCRC[2] ~= crcHi) then 
-      DEBUG("Wrong CRC in reply! "..string.format("%X", crcLo).." "..string.format("%X", crcHi)); +        DEBUG("Wrong CRC in reply! "..string.format("%X", crcLo).." "..string.format("%X", crcHi)); 
-      return false; +        return false; 
-  end  +    end 
-   + 
-  if (device.name == "DI") or (device.name == "C" then +    if (device.name == "DI") or (device.name == "C" then
       if (resp_Lentgh ~= count) then       if (resp_Lentgh ~= count) then
           ERROR("Wrong length in response");           ERROR("Wrong length in response");
           return false;           return false;
       end       end
-  else  +    else 
-      if (resp_Lentgh ~= count*2) then+        if (resp_Lentgh ~= count*2) then
           ERROR("Wrong length in response");           ERROR("Wrong length in response");
           return false;           return false;
-      end +        end 
-  end  +    end 
-                                  -- RETURN DATA --   +---------------------------------------------- RETURN DATA ---------------------------------------------- 
-    return GetHexFromTable(respData)+ 
 +    local inversion = device.xtraFields[3] 
 +    return GetHexFromTable(respData, not((inversion or 0) == 1))
  
 end -- readRegister end -- readRegister
  
 +function GetHexFromTable(inputTab, _invert)
 +    if #inputTab > 2 then
 +    local invert = _invert or false
 +        if invert then
 +            invertedInputTab = {}
 +            half1, half2 = SplitInHalf(inputTab)
  
 +            for i, v in pairs(half2) do table.insert(invertedInputTab, v) end
 +            for i, v in pairs(half1) do table.insert(invertedInputTab, v) end
 +            inputTab = invertedInputTab;
 +        end
  
 +        local toReturn = {}
 +        for i,v in pairs(inputTab) do toReturn[i]=tonumber(string.format("%X",v),16) end
 +
 +        return toReturn
 +    else
 +        -- get hex and concat it to number via string operatoin
 +                                        -- TRACE("entered GetHexFromTable with table - "..table.concat(inputTab))
 +        local numberAs_String = ""
 +        local tmpStr = ""
 +
 +            for i,v in pairs(inputTab) do
 +                tmpStr = string.format("%X",v)
 +                if (#tmpStr == 1) then
 +                    tmpStr = "0"..tmpStr
 +                end
 +                numberAs_String = numberAs_String..tmpStr
 +            end
 +                                        -- TRACE("GetHexFromTable: decimal number is "..table.concat(inputTab).." hexadecimal = ".. numberAs_String)
 +        return tonumber(numberAs_String, 16)
 +    end
 +end
 +     
 function writeRegister (reg, device, unitId, newValue) function writeRegister (reg, device, unitId, newValue)
-    -- for read-only don't write  +    local inversion = not(device.xtraFields[3] == 1 or false) 
-                                    DEBUG("Going to write this value "..newValue) + 
-    if device.name == "IR" or device.name == "DI" then +    reg.value_format = device.xtraFields[4] 
 +    if reg.dataType == DATATYPE.DW and reg.value_format == FORMAT.FLOAT_32 then 
 +        DEBUG("Going to write this value ".. (decodeIEEE754FloatToLua(newValue) or 'nil') .. ', \nuint(dec): ' .. '0d'..newValue .. ', binary: '  .. '0b'..table.concat(toBits(newValue,32)) .. ', uint(hex): ' .. string.format("0x%x", newValue)) -- TRACE("newValue = 0d" .. newValue .. hex = 0x' .. string.format("%X", newValue)) 
 +    else 
 +        DEBUG("Going to write this value "..newValue) 
 +    end 
 + 
 +    if device.name == "IR" or device.name == "DI" then
          ERROR("Can't write these type of registers (" .. device.name .. ")")          ERROR("Can't write these type of registers (" .. device.name .. ")")
-        return true  +        return true 
-    end  +    end
-    local wrRequest = {};+
          
-    -- slave address +    local wrRequest = {}; 
-    wrRequest[1] = unitId;    +    wrRequest[SLAVE_ADDR] = unitId; 
-     + 
-    -- function code +    wrRequest[FUNC_CODE] = device.xtraFields[2] 
-    wrRequest[2] = device.xtraFields[2] + 
-     +    wrRequest[REG_ADDR_HI] = GetHiByte(reg.internalAddr) 
-    -- address of register +    wrRequest[REG_ADDR_LO] = GetLoByte(reg.internalAddr) 
-    wrRequest[3] = GetHiByte(reg.internalAddr) + 
-    wrRequest[4] = GetLoByte(reg.internalAddr) +    local dataTable = GetDataAsTable(newValue, reg.dataType) 
-     +    if reg.dataType == DATATYPE.DW and inversion then 
-    local dataTable = GetDataAsTable(newValue, device.dataType) +        dataTableTmp = {} 
-         +        dataTableTmp[1]=dataTable[3]; dataTableTmp[2]=dataTable[4] 
-        local coilsTmp = 0  +        dataTableTmp[3]=dataTable[1]; dataTableTmp[4]=dataTable[2] 
-        if (device.name == "C") then  +        dataTable=dataTableTmp 
-            coilsTmp = dataTable[2] * 0xFF  +    end 
-            dataTable[2] = dataTable[1] + 
-            dataTable[1] = coilsTmp +    -- local coilsTmp = 0 
-        end  +    if (device.name == "C") then 
-    DEBUG("#dataTable now "..#dataTable) +        local coilsTmp = dataTable[2] * 0xFF 
-     +        dataTable[2] = dataTable[1] 
-    wrRequest[5] = dataTable[1] +        dataTable[1] = coilsTmp 
-    wrRequest[6] = dataTable[2] +    end 
-     +    wrRequest[DATA_LEN_HI] = dataTable[1] 
-    -- CRC+    wrRequest[DATA_LEN_LO] = dataTable[2] 
     local crc, crcLo, crcHi = 0,0,0     local crc, crcLo, crcHi = 0,0,0
     crc = GetCRC(wrRequest, 0)     crc = GetCRC(wrRequest, 0)
     crcLo = GetLoByte(crc)     crcLo = GetLoByte(crc)
-    crcHi = GetHiByte(crc)  +    crcHi = GetHiByte(crc) 
-    wrRequest[7] = crcLo +    wrRequest[CRC_POS_LO] = crcLo 
-    wrRequest[8] = crcHi  +    wrRequest[CRC_POS_HI] = crcHi 
-     +
-    DEBUG("Going to send this packet "..table.concat(wrRequest))    +
     local res = sendBytes(wrRequest);     local res = sendBytes(wrRequest);
     if (res == false) then     if (res == false) then
Line 186: Line 245:
         return false;         return false;
     end     end
-     +  
-    -- читаем ответ +    -- read response
     res = readBytes(8)      res = readBytes(8) 
-    + 
     if (res == false) then     if (res == false) then
         DEBUG("Can't receive reply")         DEBUG("Can't receive reply")
         return false         return false
     end      end 
-    +
     if (table.concat(res) ~= table.concat(wrRequest)) then      if (table.concat(res) ~= table.concat(wrRequest)) then 
         DEBUG("Response does not match!")         DEBUG("Response does not match!")
         return false         return false
     end      end 
-     +  
-    if (#dataTable == 4) then  +    if (#dataTable == DATALEN.DW) then  
-        -- DWORD+        -- TRACE("DWORD CASE!!!")
         -- repeat steps for 2nd Word          -- repeat steps for 2nd Word 
-        wrRequest[3] = GetHiByte(reg.internalAddr + 1) +        wrRequest[REG_ADDR_HI] = GetHiByte(reg.internalAddr + 1) 
-        wrRequest[4] = GetLoByte(reg.internalAddr + 1) +        wrRequest[REG_ADDR_LO] = GetLoByte(reg.internalAddr + 1) 
-        wrRequest[5] = dataTable[3] +        wrRequest[DATA_LEN_HI] = dataTable[3] 
-        wrRequest[6] = dataTable[4] +        wrRequest[DATA_LEN_LO] = dataTable[4] 
-        crc, crcLo, crcHi = GetCRC(wrRequest, 0)GetLoByte(crc) GetHiByte(crc)  +         
-        wrRequest[7] = crcLo +            crc = GetCRC(wrRequest, 2); crcLo = GetLoByte(crc); crcHi = GetHiByte(crc)  -- offset = 2 for omit crc from previous word  
-        wrRequest[8] = crcHi +             
 +            wrRequest[CRC_POS_LO] = crcLo 
 +            wrRequest[CRC_POS_HI] = crcHi  
 +        
         res = sendBytes(wrRequest);         res = sendBytes(wrRequest);
         if (res == false) then         if (res == false) then
Line 218: Line 280:
     return true     return true
 end end
- +  
- +  
--- Calculating CRC for RTU +-- Calculating CRC16 for MODBUS RTU 
 function GetCRC(req, offset)  function GetCRC(req, offset) 
-    + 
   local crc = 0xffff   local crc = 0xffff
   local mask = 0   local mask = 0
-  + 
   -- iterate bytes   -- iterate bytes
 for i=1,#req-offset do for i=1,#req-offset do
Line 238: Line 300:
               crc = bit.rshift(crc,1)               crc = bit.rshift(crc,1)
           end           end
-          + 
       end        end 
 end  end 
      return crc      return crc
 end end
 + 
 function GetHiByte(c) function GetHiByte(c)
-                                   DEBUG("Going to get high byte from "..c)+        assert(c < 65536, "This is not a two bytes!"
 +                                    -- DEBUG("Going to get high byte from "..c .. '      0x'.. string.format("%04X", c))
    return bit.rshift(c,8)    return bit.rshift(c,8)
 end end
- +  
-function GetLoByte(input_val)  +function GetLoByte(c) 
-                                    DEBUG("Going to get low byte from "..input_val+        assert(c < 65536, "This is not a two bytes!"
-    return bit.band(input_val,0xFF)+                                    -- DEBUG("Going to get  low byte from "..c .. '      0x'.. string.format("%04X", c)
 +    return bit.band(c,0xFF)
 end end
- +  
-function GetHexFromTable(inputTab) +function SplitInHalf(full
-    -- get hex and concat it to number via string operatoin  +    local h1, h2 {}, {} 
-                                    DEBUG("entered GetHexFromTable with table - "..table.concat(inputTab)+    local half math.ceil(#full/2) 
-    local numberAs_String ""  +    for i = 1half do 
-    local tmpStr ""  +        table.insert(h1full[i]
-     +    end 
-        for i,v in pairs(inputTab) do  +    for i half+1, #full do 
-            tmpStr = string.format("%X",v+        table.insert(h2, full[i]) 
-            if (#tmpStr == 1) then  +    end 
-                tmpStr = "0"..tmpStr +    return h1,h2
-            end  +
-            numberAs_String numberAs_String..tmpStr +
-        end  +
-                                DEBUG("number is "..numberAs_String.." decimal = "..numberAs_String)     +
-    return tonumber(numberAs_String16)+
 end end
  
Line 274: Line 333:
    
     local highWord, lowWord, tmpTable = 0, 0, {}     local highWord, lowWord, tmpTable = 0, 0, {}
-     +  
-    if (datatype ~= 3) then  +    if (datatype ~= DATATYPE.DW) then  
-        DEBUG("getdataastable - going to process value  "..value) +        DEBUG("GetDataAsTable - going to process value  "..value) 
-        tmpTable[1] = GetHiByte(value) ; DEBUG(tmpTable[1]) +        tmpTable[1] = GetHiByte(value) ; DEBUG('tmpTable[1] = ' .. tmpTable[1]) 
-        tmpTable[2] = GetLoByte(value) ; DEBUG(tmpTable[2])+        tmpTable[2] = GetLoByte(value) ; DEBUG('tmpTable[2] = ' .. tmpTable[2])
     else      else 
-        highWord = bit.rshift(value,16) +        highWord    = bit.rshift(value,     16) 
-        lowWord = bit.band(c,0xFFFF) +        lowWord     = bit.band  (value    0xFFFF) 
-        + 
         tmpTable[1] = GetHiByte(highWord)         tmpTable[1] = GetHiByte(highWord)
         tmpTable[2] = GetLoByte(highWord)         tmpTable[2] = GetLoByte(highWord)
         tmpTable[3] = GetHiByte(lowWord)         tmpTable[3] = GetHiByte(lowWord)
         tmpTable[4] = GetLoByte(lowWord)         tmpTable[4] = GetLoByte(lowWord)
 +        
 +        DEBUG("GetDataAsTable DW = - " .. table.concat(tmpTable))
     end      end 
     return tmpTable     return tmpTable
 end end
 +
 +function toBits(num,bits)
 + -- returns a table of bits, most significant first.
 + bits = bits or math.max(1, select(2, math.frexp(num)))
 + local t = {} -- will contain the bits
 + for b = bits, 1, -1 do
 + t[b] = math.fmod(num, 2)
 + num = math.floor((num - t[b]) / 2)
 + end
 + return t
 +end
 +
 +
 +function Bin2Hex(s)
 +    assert (type(s) == "string", "binary as string expected")
 +    -- s -> binary string
 +    local bin2hex = {
 +    ["0000"] = "0", ["0001"] = "1", ["0010"] = "2", ["0011"] = "3",
 +    ["0100"] = "4", ["0101"] = "5", ["0110"] = "6", ["0111"] = "7",
 +    ["1000"] = "8", ["1001"] = "9", ["1010"] = "A", ["1011"] = "B",
 +    ["1100"] = "C", ["1101"] = "D", ["1110"] = "E", ["1111"] = "F"
 + }
 +
 +    tabBytes={}
 +    
 +    local l = 0
 +    local h, b = "", ""
 +    local rem
 +    
 +    l = string.len(s)
 +    rem = l % 4
 +    l = l-1
 +
 + -- need to prepend zeros to eliminate mod 4
 + if (rem > 0) then
 + s = string.rep("0", 4 - rem)..s
 + end
 +
 + for i = 1, l, 4 do
 + b = string.sub(s, i, i+3)
 + table.insert(tabBytes,bin2hex[b])
 + if not b then ERROR("bin2hex b is nil") end
 + TRACE("bin2hex b = " .. tostring(b))
 + h = h..bin2hex[b]
 + end
 +
 + return h, tabBytes
 +end
 +
 +
 +function hex2dec(hexstr)
 + return tonumber(hexstr,16)
 +end
 +
 +function decodeIEEE754FloatToLua(input)
 +    sign = input < 0 and -1 or 1 
 +    input = math.abs(input)
 +    
 + bitsTable = toBits(input,32)
 +    
 + exponentTable={table.unpack(bitsTable, 2,9)} 
 + mantissaTable={table.unpack(bitsTable,10)}
 +
 + mantissaStr, manstissaTabBytes = Bin2Hex(table.concat(mantissaTable))
 + mantissaNum = hex2dec(table.concat(manstissaTabBytes))  
 +
 +
 + exponentStr, exponentBytesTable = Bin2Hex(table.concat(exponentTable)) 
 + exponentNum =  hex2dec(table.concat(exponentBytesTable)) 
 +
 + exponent = exponentNum - 127 -- 0 - 127 = -127 --> denormalized mode
 + mantissa=mantissaNum/8388608
 +
 + if exponent == -127 then -- denormalized mode
 + exponent = -126
 + mantissa = mantissa
 + else
 + mantissa = mantissa + 1
 + end
 +
 + float_number=math.ldexp(mantissa,exponent) * sign
 + return float_number
 +end
 +
 </code> </code>
  
  
modbus_rtu_custom.txt · Last modified: 2023/11/30 10:01 by emozolyak

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki