Browse Source

DS18B20 Lua module (#3150)

Lukáš Voborský 3 years ago
parent
commit
b9b5815e97
3 changed files with 219 additions and 31 deletions
  1. 3 2
      docs/lua-modules/ds18b20.md
  2. 209 0
      lua_modules/ds18b20/ds18b20-integer.lua
  3. 7 29
      lua_modules/ds18b20/ds18b20.lua

+ 3 - 2
docs/lua-modules/ds18b20.md

@@ -5,8 +5,9 @@
 
 This Lua module provides access to [DS18B20](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf) 1-Wire digital thermometer.
 
-!!! note
-	The module requires `ow` C module built into firmware.
+For integer version of firmware use [ds18b20-integer.lua](../../lua_modules/ds18b20/ds18b20-integer.lua) module - measured temperatures are multiplied by 10000.
+
+The module requires `ow` C module built into firmware.
 
 ### Require
 ```lua

+ 209 - 0
lua_modules/ds18b20/ds18b20-integer.lua

@@ -0,0 +1,209 @@
+--------------------------------------------------------------------------------
+-- DS18B20 one wire module for NODEMCU
+-- NODEMCU TEAM
+-- LICENCE: http://opensource.org/licenses/MIT
+-- @voborsky, @devsaurus, TerryE  26 Mar 2017
+--------------------------------------------------------------------------------
+local modname = ...
+
+-- Used modules and functions
+local type, tostring, pcall, ipairs =
+      type, tostring, pcall, ipairs
+-- Local functions
+local ow_setup, ow_search, ow_select, ow_read, ow_read_bytes, ow_write, ow_crc8,
+        ow_reset, ow_reset_search, ow_skip, ow_depower =
+      ow.setup, ow.search, ow.select, ow.read, ow.read_bytes, ow.write, ow.crc8,
+        ow.reset, ow.reset_search, ow.skip, ow.depower
+
+local node_task_post, node_task_LOW_PRIORITY = node.task.post, node.task.LOW_PRIORITY
+local string_char, string_dump = string.char, string.dump
+local now, tmr_create, tmr_ALARM_SINGLE = tmr.now, tmr.create, tmr.ALARM_SINGLE
+local table_sort, table_concat = table.sort, table.concat
+local file_open = file.open
+local conversion
+
+local DS18B20FAMILY   = 0x28
+local DS1920FAMILY    = 0x10  -- and DS18S20 series
+local CONVERT_T       = 0x44
+local READ_SCRATCHPAD = 0xBE
+local READ_POWERSUPPLY= 0xB4
+local MODE = 1
+
+local pin, cb, unit = 3
+local status = {}
+
+local debugPrint = function() return end
+
+--------------------------------------------------------------------------------
+-- Implementation
+--------------------------------------------------------------------------------
+local function enable_debug()
+  debugPrint = function (...) print(now(),' ', ...) end
+end
+
+local function to_string(addr, esc)
+  if type(addr) == 'string' and #addr == 8 then
+    return ( esc == true and
+             '"\\%u\\%u\\%u\\%u\\%u\\%u\\%u\\%u"' or
+             '%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X '):format(addr:byte(1,8))
+  else
+    return tostring(addr)
+  end
+end
+
+local function readout(self)
+  local next = false
+  local sens = self.sens
+  local temp = self.temp
+  for i, s in ipairs(sens) do
+    if status[i] == 1 then
+      ow_reset(pin)
+      local addr = s:sub(1,8)
+      ow_select(pin, addr)   -- select the  sensor
+      ow_write(pin, READ_SCRATCHPAD, MODE)
+      local data = ow_read_bytes(pin, 9)
+
+      local t=(data:byte(1)+data:byte(2)*256)
+      -- t is actually signed so process the sign bit and adjust for fractional bits
+      -- the DS18B20 family has 4 fractional bits and the DS18S20s, 1 fractional bit
+      t = ((t <= 32767) and t or t - 65536) *
+          ((addr:byte(1) == DS18B20FAMILY) and 625 or 5000)
+      local crc, b9 = ow_crc8(string.sub(data,1,8)), data:byte(9)
+
+      if unit == 'F' then
+        t = (t * 18)/10 + 320000
+      elseif unit == 'K' then
+        t = t + 2731500
+      end
+      local sgn = t<0 and -1 or 1
+      local tA = sgn*t
+      local tH=tA/10000
+      local tL=(tA%10000)/1000 + ((tA%1000)/100 >= 5 and 1 or 0)
+
+      if tH and (t~=850000) then
+        debugPrint(to_string(addr),(sgn<0 and "-" or "")..tH.."."..tL, crc, b9)
+        if crc==b9 then temp[addr]=t end
+        status[i] = 2
+      end
+    end
+    next = next or status[i] == 0
+  end
+  if next then
+    node_task_post(node_task_LOW_PRIORITY, function() return conversion(self) end)
+  else
+    --sens = {}
+    if cb then
+      node_task_post(node_task_LOW_PRIORITY, function() return cb(temp) end)
+    end
+  end
+end
+
+conversion = (function (self)
+  local sens = self.sens
+  local powered_only = true
+  for _, s in ipairs(sens) do powered_only = powered_only and s:byte(9) ~= 1 end
+  if powered_only then
+    debugPrint("starting conversion: all sensors")
+    ow_reset(pin)
+    ow_skip(pin)  -- skip ROM selection, talk to all sensors
+    ow_write(pin, CONVERT_T, MODE)  -- and start conversion
+    for i, _ in ipairs(sens) do status[i] = 1 end
+  else
+    local started = false
+    for i, s in ipairs(sens) do
+      if status[i] == 0 then
+        local addr, parasite = s:sub(1,8), s:byte(9) == 1
+        if parasite and started then break end -- do not start concurrent conversion of powered and parasite
+        debugPrint("starting conversion:", to_string(addr), parasite and "parasite" or "")
+        ow_reset(pin)
+        ow_select(pin, addr)  -- select the sensor
+        ow_write(pin, CONVERT_T, MODE)  -- and start conversion
+        status[i] = 1
+        if parasite then break end -- parasite sensor blocks bus during conversion
+        started = true
+      end
+    end
+  end
+  tmr_create():alarm(750, tmr_ALARM_SINGLE, function() return readout(self) end)
+end)
+
+local function _search(self, lcb, lpin, search, save)
+  self.temp = {}
+  if search then self.sens = {}; status = {} end
+  local sens = self.sens
+  pin = lpin or pin
+
+  local addr
+  if not search and #sens == 0 then
+    -- load addreses if available
+    debugPrint ("geting addreses from flash")
+    local s,check,a = pcall(dofile, "ds18b20_save.lc")
+    if s and check == "ds18b20" then
+      for i = 1, #a do sens[i] = a[i] end
+    end
+    debugPrint (#sens, "addreses found")
+  end
+
+  ow_setup(pin)
+  if search or #sens == 0 then
+    ow_reset_search(pin)
+    -- ow_target_search(pin,0x28)
+    -- search the first device
+    addr = ow_search(pin)
+  else
+    for i, _ in ipairs(sens) do status[i] = 0 end
+  end
+  local function cycle()
+    if addr then
+      local crc=ow_crc8(addr:sub(1,7))
+      if (crc==addr:byte(8)) and ((addr:byte(1)==DS1920FAMILY) or (addr:byte(1)==DS18B20FAMILY)) then
+        ow_reset(pin)
+        ow_select(pin, addr)
+        ow_write(pin, READ_POWERSUPPLY, MODE)
+        local parasite = (ow_read(pin)==0 and 1 or 0)
+        sens[#sens+1]= addr..string_char(parasite)
+        status[#sens] = 0
+        debugPrint("contact: ", to_string(addr), parasite == 1 and "parasite" or "")
+      end
+      addr = ow_search(pin)
+      node_task_post(node_task_LOW_PRIORITY, cycle)
+    else
+      ow_depower(pin)
+      -- place powered sensors first
+      table_sort(sens, function(a, b) return a:byte(9)<b:byte(9) end) -- parasite
+      -- save sensor addreses
+      if save then
+        debugPrint ("saving addreses to flash")
+
+        local addr_list = {}
+        for i =1, #sens do
+          local s = sens[i]
+          addr_list[i] = to_string(s:sub(1,8), true)..('.."\\%u"'):format(s:byte(9))
+        end
+        local save_statement = 'return "ds18b20", {' .. table_concat(addr_list, ',') .. '}'
+        debugPrint (save_statement)
+        local save_file = file_open("ds18b20_save.lc","w")
+        save_file:write(string_dump(loadstring(save_statement)))
+        save_file:close()
+      end
+      -- end save sensor addreses
+      if lcb then node_task_post(node_task_LOW_PRIORITY, lcb) end
+    end
+  end
+  cycle()
+end
+
+local function read_temp(self, lcb, lpin, lunit, force_search, save_search)
+  cb, unit = lcb, lunit or unit
+  _search(self, function() return conversion(self) end, lpin, force_search, save_search)
+end
+
+ -- Set module name as parameter of require and return module table
+local M = {
+  sens = {},
+  temp = {},
+  C = 'C', F = 'F', K = 'K',
+  read_temp = read_temp, enable_debug = enable_debug
+}
+_G[modname or 'ds18b20'] = M
+return M

+ 7 - 29
lua_modules/ds18b20/ds18b20.lua

@@ -71,38 +71,16 @@ local function readout(self)
           ((addr:byte(1) == DS18B20FAMILY) and 625 or 5000)
       local crc, b9 = ow_crc8(string.sub(data,1,8)), data:byte(9)
 
-      if 1/2 == 0 then
-        -- integer version
+      t = t / 10000
+      if math_floor(t)~=85 then
         if unit == 'F' then
-          t = (t * 18)/10 + 320000
+          t = t * 18/10 + 32
         elseif unit == 'K' then
-          t = t + 2731500
+          t = t + 27315/100
         end
-        local sgn = t<0 and -1 or 1
-        local tA = sgn*t
-        local tH=tA/10000
-        local tL=(tA%10000)/1000 + ((tA%1000)/100 >= 5 and 1 or 0)
-
-        if tH and (t~=850000) then
-          debugPrint(to_string(addr),(sgn<0 and "-" or "")..tH.."."..tL, crc, b9)
-          if crc==b9 then temp[addr]=(sgn<0 and "-" or "")..tH.."."..tL end
-          status[i] = 2
-        end
-        -- end integer version
-      else
-        -- float version
-        t = t / 10000
-        if math_floor(t)~=85 then
-          if unit == 'F' then
-            t = t * 18/10 + 32
-          elseif unit == 'K' then
-            t = t + 27315/100
-          end
-          debugPrint(to_string(addr), t, crc, b9)
-          if crc==b9 then temp[addr]=t end
-          status[i] = 2
-        end
-        -- end float version
+        debugPrint(to_string(addr), t, crc, b9)
+        if crc==b9 then temp[addr]=t end
+        status[i] = 2
       end
     end
     next = next or status[i] == 0