-------------------------------------------------------------------------------- -- 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 math_floor = math.floor 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 1/2 == 0 then -- integer version 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]=(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 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)