ds18b20.lua 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. --------------------------------------------------------------------------------
  2. -- DS18B20 one wire module for NODEMCU
  3. -- NODEMCU TEAM
  4. -- LICENCE: http://opensource.org/licenses/MIT
  5. -- @voborsky, @devsaurus, TerryE 26 Mar 2017
  6. ----------------------------------------------------------------------------------------------------------------------------------------------------------------
  7. local modname = ...
  8. -- Used modules and functions
  9. local table, string, ow, tmr, print, type, tostring, pcall, ipairs =
  10. table, string, ow, tmr, print, type, tostring, pcall, ipairs
  11. -- Local functions
  12. 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 =
  13. ow.setup, ow.search, ow.select, ow.read, ow.read_bytes, ow.write, ow.crc8, ow.reset, ow.reset_search, ow.skip, ow.depower
  14. local node_task_post, node_task_LOW_PRIORITY = node.task.post, node.task.LOW_PRIORITY
  15. local string_char, string_dump = string.char, string.dump
  16. local now, tmr_create, tmr_ALARM_SINGLE = tmr.now, tmr.create, tmr.ALARM_SINGLE
  17. local table_sort, table_concat = table.sort, table.concat
  18. local math_floor = math.floor
  19. local file_open = file.open
  20. table, string, tmr, ow = nil, nil, nil, nil
  21. local DS18B20FAMILY = 0x28
  22. local DS1920FAMILY = 0x10 -- and DS18S20 series
  23. local CONVERT_T = 0x44
  24. local READ_SCRATCHPAD = 0xBE
  25. local READ_POWERSUPPLY= 0xB4
  26. local MODE = 1
  27. local pin, cb, unit = 3
  28. local status = {}
  29. local debugPrint = function() return end
  30. --------------------------------------------------------------------------------
  31. -- Implementation
  32. --------------------------------------------------------------------------------
  33. local function enable_debug()
  34. debugPrint = function (...) print(now(),' ', ...) end
  35. end
  36. local function to_string(addr, esc)
  37. if type(addr) == 'string' and #addr == 8 then
  38. return ( esc == true and
  39. '"\\%u\\%u\\%u\\%u\\%u\\%u\\%u\\%u"' or
  40. '%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X '):format(addr:byte(1,8))
  41. else
  42. return tostring(addr)
  43. end
  44. end
  45. local function readout(self)
  46. local next = false
  47. local sens = self.sens
  48. local temp = self.temp
  49. for i, s in ipairs(sens) do
  50. if status[i] == 1 then
  51. ow_reset(pin)
  52. local addr = s:sub(1,8)
  53. ow_select(pin, addr) -- select the sensor
  54. ow_write(pin, READ_SCRATCHPAD, MODE)
  55. data = ow_read_bytes(pin, 9)
  56. local t=(data:byte(1)+data:byte(2)*256)
  57. -- t is actually signed so process the sign bit and adjust for fractional bits
  58. -- the DS18B20 family has 4 fractional bits and the DS18S20s, 1 fractional bit
  59. t = ((t <= 32767) and t or t - 65536) *
  60. ((addr:byte(1) == DS18B20FAMILY) and 625 or 5000)
  61. if 1/2 == 0 then
  62. -- integer version
  63. if unit == 'F' then
  64. t = (t * 18)/10 + 320000
  65. elseif unit == 'K' then
  66. t = t + 2731500
  67. end
  68. local sgn = t<0 and -1 or 1
  69. local tA = sgn*t
  70. local tH=tA/10000
  71. local tL=(tA%10000)/1000 + ((tA%1000)/100 >= 5 and 1 or 0)
  72. if tH and (t~=850000) then
  73. temp[addr]=(sgn<0 and "-" or "")..tH.."."..tL
  74. debugPrint(to_string(addr),(sgn<0 and "-" or "")..tH.."."..tL)
  75. status[i] = 2
  76. end
  77. -- end integer version
  78. else
  79. -- float version
  80. if t and (math_floor(t/10000)~=85) then
  81. t = t / 10000
  82. if unit == 'F' then
  83. t = t * 18/10 + 32
  84. elseif unit == 'K' then
  85. t = t + 27315/100
  86. end
  87. self.temp[addr]=t
  88. debugPrint(to_string(addr), t)
  89. status[i] = 2
  90. end
  91. -- end float version
  92. end
  93. end
  94. next = next or status[i] == 0
  95. end
  96. if next then
  97. node_task_post(node_task_LOW_PRIORITY, function() return conversion(self) end)
  98. else
  99. --sens = {}
  100. if cb then
  101. node_task_post(node_task_LOW_PRIORITY, function() return cb(temp) end)
  102. end
  103. end
  104. end
  105. local function conversion(self)
  106. local sens = self.sens
  107. local powered_only = true
  108. for _, s in ipairs(sens) do powered_only = powered_only and s:byte(9) ~= 1 end
  109. if powered_only then
  110. debugPrint("starting conversion: all sensors")
  111. ow_reset(pin)
  112. ow_skip(pin) -- select the sensor
  113. ow_write(pin, CONVERT_T, MODE) -- and start conversion
  114. for i, s in ipairs(sens) do status[i] = 1 end
  115. else
  116. for i, s in ipairs(sens) do
  117. if status[i] == 0 then
  118. local addr, parasite = s:sub(1,8), s:byte(9)
  119. debugPrint("starting conversion:", to_string(addr), parasite == 1 and "parasite" or " ")
  120. ow_reset(pin)
  121. ow_select(pin, addr) -- select the sensor
  122. ow_write(pin, CONVERT_T, MODE) -- and start conversion
  123. status[i] = 1
  124. if parasite == 1 then break end -- parasite sensor blocks bus during conversion
  125. end
  126. end
  127. end
  128. tmr_create():alarm(750, tmr_ALARM_SINGLE, function() return readout(self) end)
  129. end
  130. local function _search(self, lcb, lpin, search, save)
  131. self.temp = {}
  132. if search then self.sens = {}; status = {} end
  133. local temp = self.temp
  134. local sens = self.sens
  135. pin = lpin or pin
  136. local addr
  137. if not search and #sens == 0 then
  138. -- load addreses if available
  139. debugPrint ("geting addreses from flash")
  140. local s,check,a = pcall(dofile, "ds18b20_save.lc")
  141. if s and check == "ds18b20" then
  142. for i = 1, #a do sens[i] = a[i] end
  143. end
  144. debugPrint (#sens, "addreses found")
  145. end
  146. ow_setup(pin)
  147. if search or #sens == 0 then
  148. ow_reset_search(pin)
  149. -- ow_target_search(pin,0x28)
  150. -- search the first device
  151. addr = ow_search(pin)
  152. else
  153. for i, s in ipairs(sens) do status[i] = 0 end
  154. end
  155. local function cycle()
  156. debugPrint("cycle")
  157. if addr then
  158. local crc=ow_crc8(addr:sub(1,7))
  159. if (crc==addr:byte(8)) and ((addr:byte(1)==DS1920FAMILY) or (addr:byte(1)==DS18B20FAMILY)) then
  160. ow_reset(pin)
  161. ow_select(pin, addr)
  162. ow_write(pin, READ_POWERSUPPLY, MODE)
  163. local parasite = (ow_read(pin)==0 and 1 or 0)
  164. sens[#sens+1]= addr..string_char(parasite) -- {addr=addr, parasite=parasite, status=0}
  165. debugPrint("contact: ", to_string(addr), parasite == 1 and "parasite" or " ")
  166. end
  167. addr = ow_search(pin)
  168. node_task_post(node_task_LOW_PRIORITY, cycle)
  169. else
  170. ow_depower(pin)
  171. -- place powered sensors first
  172. table_sort(sens, function(a, b) return a:byte(9)<b:byte(9) end) -- parasite
  173. -- save sensor addreses
  174. if save then
  175. debugPrint ("saving addreses to flash")
  176. local addr_list = {}
  177. for i =1, #sens do
  178. local s = sens[i]
  179. addr_list[i] = to_string(s:sub(1,8), true)..('.."\\%u"'):format(s:byte(9))
  180. end
  181. local save_statement = 'return "ds18b20", {' .. table_concat(addr_list, ',') .. '}'
  182. debugPrint (save_statement)
  183. local save_file = file_open("ds18b20_save.lc","w")
  184. save_file:write(string_dump(loadstring(save_statement)))
  185. save_file:close()
  186. end
  187. -- end save sensor addreses
  188. if lcb then node_task_post(node_task_LOW_PRIORITY, lcb) end
  189. end
  190. end
  191. cycle()
  192. end
  193. local function read_temp(self, lcb, lpin, lunit, force_search, save_search)
  194. cb, unit = lcb, lunit or unit
  195. _search(self, function() return conversion(self) end, lpin, force_search, save_search)
  196. end
  197. -- Set module name as parameter of require and return module table
  198. local M = {
  199. sens = {},
  200. temp = {},
  201. C = 'C', F = 'F', K = 'K',
  202. read_temp = read_temp, enable_debug = enable_debug
  203. }
  204. _G[modname or 'ds18b20'] = M
  205. return M