From 52f41a840c5f09fb21b5957a436beeeef65b8992 Mon Sep 17 00:00:00 2001 From: vsky Date: Sat, 25 Feb 2017 22:56:45 +0100 Subject: [PATCH] Re-write DS18B20 asynchronous example (#1820) --- lua_modules/ds18b20/ds18b20-example.lua | 41 ++-- lua_modules/ds18b20/ds18b20-web.lua | 43 +++-- lua_modules/ds18b20/ds18b20.EN.md | 161 +++------------- lua_modules/ds18b20/ds18b20.ZH.md | 160 ---------------- lua_modules/ds18b20/ds18b20.lua | 238 +++++++++++------------- 5 files changed, 182 insertions(+), 461 deletions(-) delete mode 100644 lua_modules/ds18b20/ds18b20.ZH.md diff --git a/lua_modules/ds18b20/ds18b20-example.lua b/lua_modules/ds18b20/ds18b20-example.lua index c349e43a..d8ab621b 100644 --- a/lua_modules/ds18b20/ds18b20-example.lua +++ b/lua_modules/ds18b20/ds18b20-example.lua @@ -1,27 +1,26 @@ +-- encoder module is needed only for debug output; lines can be removed if no +-- debug output is needed and/or encoder module is missing + t = require("ds18b20") +pin = 3 -- gpio0 = 3, gpio2 = 4 --- ESP-01 GPIO Mapping -gpio0 = 3 -gpio2 = 4 +function readout(temp) + for addr, temp in pairs(temp) do + -- print(string.format("Sensor %s: %s 'C", addr, temp)) + print(string.format("Sensor %s: %s 'C", encoder.toBase64(addr), temp)) -- readable address with base64 encoding is preferred when encoder module is available + end -t.setup(gpio0) -addrs = t.addrs() -if (addrs ~= nil) then - print("Total DS18B20 sensors: "..table.getn(addrs)) + -- Module can be released when it is no longer needed + t = nil + package.loaded["ds18b20"]=nil end --- Just read temperature -print("Temperature: "..t.read().."'C") - --- Get temperature of first detected sensor in Fahrenheit -print("Temperature: "..t.read(nil,t.F).."'F") - --- Query the second detected sensor, get temperature in Kelvin -if (table.getn(addrs) >= 2) then - print("Second sensor: "..t.read(addrs[2],t.K).."'K") +-- t:readTemp(readout) -- default pin value is 3 +t:readTemp(readout, pin) +if t.sens then + print("Total number of DS18B20 sensors: "..table.getn(t.sens)) + for i, s in ipairs(t.sens) do + -- print(string.format(" sensor #%d address: %s%s", i, s.addr, s.parasite == 1 and " (parasite)" or "")) + print(string.format(" sensor #%d address: %s%s", i, encoder.toBase64(s.addr), s.parasite == 1 and " (parasite)" or "")) -- readable address with base64 encoding is preferred when encoder module is available + end end - --- Don't forget to release it after use -t = nil -ds18b20 = nil -package.loaded["ds18b20"]=nil diff --git a/lua_modules/ds18b20/ds18b20-web.lua b/lua_modules/ds18b20/ds18b20-web.lua index 7ff81979..41182dfd 100644 --- a/lua_modules/ds18b20/ds18b20-web.lua +++ b/lua_modules/ds18b20/ds18b20-web.lua @@ -1,27 +1,36 @@ -require('ds18b20') +t = require('ds18b20') port = 80 +pin = 3 -- gpio0 = 3, gpio2 = 4 +gconn = {} -- global variable for connection --- ESP-01 GPIO Mapping -gpio0, gpio2 = 3, 4 +function readout(temp) + local resp = "HTTP/1.1 200 OK\nContent-Type: text/html\nRefresh: 5\n\n" .. + "" .. + "" .. + "ESP8266
" + + for addr, temp in pairs(temp) do + -- resp = resp .. string.format("Sensor %s: %s ℃
", addr, temp) + resp = resp .. string.format("Sensor %s: %s ℃
", encoder.toBase64(addr), temp) -- readable address with base64 encoding is preferred when encoder module is available + end + + resp = resp .. + "Node ChipID: " .. node.chipid() .. "
" .. + "Node MAC: " .. wifi.sta.getmac() .. "
" .. + "Node Heap: " .. node.heap() .. "
" .. + "Timer Ticks: " .. tmr.now() .. "
" .. + "" -ds18b20.setup(gpio0) + gconn:send(resp) + gconn:on("sent",function(conn) conn:close() end) +end srv=net.createServer(net.TCP) srv:listen(port, function(conn) - conn:send("HTTP/1.1 200 OK\nContent-Type: text/html\nRefresh: 5\n\n" .. - "" .. - "" .. - "ESP8266
" .. - "Temperature : " .. ds18b20.read() .. "
" .. - "Node ChipID : " .. node.chipid() .. "
" .. - "Node MAC : " .. wifi.sta.getmac() .. "
" .. - "Node Heap : " .. node.heap() .. "
" .. - "Timer Ticks : " .. tmr.now() .. "
" .. - "") - conn:on("sent",function(conn) conn:close() end) + gconn = conn + -- t:readTemp(readout) -- default pin value is 3 + t:readTemp(readout, pin) end ) - - diff --git a/lua_modules/ds18b20/ds18b20.EN.md b/lua_modules/ds18b20/ds18b20.EN.md index bdf69397..155bc29d 100644 --- a/lua_modules/ds18b20/ds18b20.EN.md +++ b/lua_modules/ds18b20/ds18b20.EN.md @@ -8,150 +8,47 @@ ds18b20 = require("ds18b20") ds18b20 = nil package.loaded["ds18b20"]=nil ``` -##Constant -C, F, K - -##setup() -####Description -Setting the pin of DS18B20.
+ +##readTemp() +Scans the bus for DS18B20 sensors, starts a readout (conversion) for all sensors and calls a callback function when all temperatures are available. Powered sensors are read at once first. Parasit-powered sensors are read one by one. The first parasit-powered sensor is read together with all powered sensors. + +The module requires `ow` module. + +The also module uses `encoder` module for printing debug information with more readable representation of sensor address (`encoder.toBase64()`). ####Syntax -setup(pin) +`readTemp(callback, pin)` ####Parameters -pin: 1~10, IO index. If parameter is nil, it will use pin 9(GPIO2) automatically.
+- `callback` function that receives all results when all conversions finish. The callback funciton has one parameter - an array addressed by sensor addresses and a value of the temperature (string for integer version). +- `pin` pin of the one-wire bus. If nil, GPIO0 (3) is used. ####Returns nil ####Example ```lua -ds18b20 = require("ds18b20") -ds18b20.setup(9) --- Don't forget to release it after use -ds18b20 = nil -package.loaded["ds18b20"]=nil -``` +t = require("ds18b20") +pin = 3 -- gpio0 = 3, gpio2 = 4 -####See also -**-** []() +function readout(temp) + for addr, temp in pairs(temp) do + print(string.format("Sensor %s: %s 'C", encoder.toBase64(addr), temp)) + end - - -## addrs() -####Description -Return a table contain all of the addresses of DS18B20 on one-wire. If the setup(pin) function not executed, the pin 9(GPIO2) will be initialized as one-wire mode automatically.
- -####Syntax -addrs() - -####Parameters -nil -####Returns -addrs: A table contain all of the addresses of DS18B20 on one-wire. Every address is a string. If failed, it will be nil.
- -####Example -```lua -ds18b20 = require("ds18b20") -ds18b20.setup(9) -addrs = ds18b20.addrs() -if (addrs ~= nil) then - print("Total DS18B20 sensors: "..table.getn(addrs)) + -- Module can be released when it is no longer needed + t = nil + package.loaded["ds18b20"]=nil +end + +-- t:readTemp(readout) -- default pin value is 3 +t:readTemp(readout, pin) +if t.sens then + print("Total number of DS18B20 sensors: "..table.getn(t.sens)) + for i, s in ipairs(t.sens) do + -- print(string.format(" sensor #%d address: %s%s", i, s.addr, s.parasite == 1 and " (parasite)" or "")) + print(string.format(" sensor #%d address: %s%s", i, encoder.toBase64(s.addr), s.parasite == 1 and " (parasite)" or "")) -- readable address with base64 encoding is preferred when encoder module is available + end end --- Don't forget to release it after use -ds18b20 = nil -package.loaded["ds18b20"]=nil ``` - -####See also -**-** []() - - - -## readNumber() -####Description -Read the value of temperature. If the setup(pin) function not executed, the pin 9(GPIO2) will be initialized as one-wire mode automatically.
- -####Syntax -readNumber(addr, unit) - -####Parameters -addr: string, the address of DS18B20. It will select the first address which be found when this parameter is nil.
-unit: integer, unit conversion. Only Constant is acceptable, such as C(Celsius),F(Fahrenheit) and K(Kelvin). If this parameter is nil, the constant C(Celsius) will be selected automatically.
- -####Returns -t1: integer. The integer part of the temperature. If it read fails, return nil.
-t2: integer. The fractional part of the temperature. If it read fails, return nil.
- -####Example -```lua -t=require("ds18b20") -t.setup(9) -addrs=t.addrs() --- Total DS18B20 numbers, assume it is 2 -print(table.getn(addrs)) --- The first DS18B20 -print(t.readNumber(addrs[1],t.C)) -print(t.readNumber(addrs[1],t.F)) -print(t.readNumber(addrs[1],t.K)) --- The second DS18B20 -print(t.readNumber(addrs[2],t.C)) -print(t.readNumber(addrs[2],t.F)) -print(t.readNumber(addrs[2],t.K)) --- Just read -print(t.readNumber()) --- Just read as fahrenheit -print(t.readNumber(nil,t.F)) --- Read as values -t1, t2 = t.readNumber() --- Don't forget to release it after use -t = nil -ds18b20 = nil -package.loaded["ds18b20"]=nil -``` -####See also -**-** []() - - -## read() -####Description -Read the string of temperature. If the setup(pin) function not executed, the pin 9(GPIO2) will be initialized as one-wire mode automatically.
- -####Syntax -read(addr, unit) - -####Parameters -addr: string, the address of DS18B20. It will select the first address which be found when this parameter is nil.
-unit: integer, unit conversion. Only Constant is acceptable, such as C(Celsius),F(Fahrenheit) and K(Kelvin). If this parameter is nil, the constant C(Celsius) will be selected automatically.
- -####Returns -t: string. The string of the temperature. If it read fails, return nil.
- -####Example -```lua -t=require("ds18b20") -t.setup(9) -addrs=t.addrs() --- Total DS18B20 numbers, assume it is 2 -print(table.getn(addrs)) --- The first DS18B20 -print(t.read(addrs[1],t.C)) -print(t.read(addrs[1],t.F)) -print(t.read(addrs[1],t.K)) --- The second DS18B20 -print(t.read(addrs[2],t.C)) -print(t.read(addrs[2],t.F)) -print(t.read(addrs[2],t.K)) --- Just read -print(t.read()) --- Just read as centigrade -print(t.read(nil,t.C)) --- Don't forget to release it after use -t = nil -ds18b20 = nil -package.loaded["ds18b20"]=nil -``` -####See also -**-** []() - diff --git a/lua_modules/ds18b20/ds18b20.ZH.md b/lua_modules/ds18b20/ds18b20.ZH.md deleted file mode 100644 index c6092714..00000000 --- a/lua_modules/ds18b20/ds18b20.ZH.md +++ /dev/null @@ -1,160 +0,0 @@ -#DS18B20 模块 -##引用 -```lua -ds18b20 = require("ds18b20") -``` -#释放 -```lua -ds18b20 = nil -package.loaded["ds18b20"]=nil -``` -##常量 -C, F, K - - -##setup() -####描述 -设置DS18B20所在的管脚(pin)。
- -####语法 -setup(pin) - -####参数 -pin: 1~10, IO 编号。如果参数为nil,会自动设定为9(GPIO2).
- -####返回值 -nil - -####示例 -```lua -ds18b20 = require("ds18b20") -ds18b20.setup(9) --- Don't forget to release it after use -ds18b20 = nil -package.loaded["ds18b20"]=nil - -``` - -####参见 -**-** []() - - - -## addrs() -####描述 -返回单总线上所有DS18B20器件的地址列表(table)。如果没有执行过setup(pin),则会自动对引脚9(GPIO2)进行单总线模式初始化。
- -####语法 -addrs() - -####参数 -nil -####返回值 -addrs: 返回包含单总线上所有DS18B20器件的地址列表(table)。其中地址是字符串类型(String)。如果失败则返回nil.
- -####示例 -```lua -ds18b20 = require("ds18b20") -ds18b20.setup(9) -addrs = ds18b20.addrs() -if (addrs ~= nil) then - print("Total DS18B20 sensors: "..table.getn(addrs)) -end --- Don't forget to release it after use -ds18b20 = nil -package.loaded["ds18b20"]=nil - -``` - -####参见 -**-** []() - - - -## readNumber() -####描述 -读取温度数值。如果没有执行过setup(pin),则会自动对引脚9(GPIO2)进行单总线模式初始化。
- -####语法 -readNumber(addr, unit) - -####参数 -addr: 字符串, DS18B20地址。 如果该参数为nil,会自动选择第一个发现的地址。
-unit: 单位转换,只接受常量C(摄氏度),F(华氏度), K(开氏度)。如果该参数为nil,会自动选择常量C(摄氏度) 。
- -####返回值 -t1: 数值,温度的整数部分。如果读取失败返回nil.
-t2: 数值,温度的小数部分。如果读取失败返回nil.
- -####示例 -```lua -t=require("ds18b20") -t.setup(9) -addrs=t.addrs() --- Total DS18B20 numbers, assume it is 2 -print(table.getn(addrs)) --- The first DS18B20 -print(t.readNumber(addrs[1],t.C)) -print(t.readNumber(addrs[1],t.F)) -print(t.readNumber(addrs[1],t.K)) --- The second DS18B20 -print(t.readNumber(addrs[2],t.C)) -print(t.readNumber(addrs[2],t.F)) -print(t.readNumber(addrs[2],t.K)) --- Just read -print(t.readNumber()) --- Just read as fahrenheit -print(t.readNumber(nil,t.F)) --- Read as values -t1, t2 = t.readNumber() --- Don't forget to release it after use -t = nil -ds18b20 = nil -package.loaded["ds18b20"]=nil - -``` -####参见 -**-** []() - - -## read() -####描述 -读取温度字符串。如果没有执行过setup(pin),则会自动对引脚9(GPIO2)进行单总线模式初始化。
- -####语法 -read(addr, unit) - -####参数 -addr: 字符串, DS18B20地址。 如果该参数为nil,会自动选择第一个发现的地址。
-unit: 单位转换,只接受常量C(摄氏度),F(华氏度), K(开氏度)。如果该参数为nil,会自动选择常量C(摄氏度) 。
- -####返回值 -t: 字符串,表示成字符串形式的温度。如果读取失败返回nil.
- -####示例 -```lua -t=require("ds18b20") -t.setup(9) -addrs=t.addrs() --- Total DS18B20 numbers, assume it is 2 -print(table.getn(addrs)) --- The first DS18B20 -print(t.read(addrs[1],t.C)) -print(t.read(addrs[1],t.F)) -print(t.read(addrs[1],t.K)) --- The second DS18B20 -print(t.read(addrs[2],t.C)) -print(t.read(addrs[2],t.F)) -print(t.read(addrs[2],t.K)) --- Just read -print(t.read()) --- Just read as centigrade -print(t.read(nil,t.C)) --- Don't forget to release it after use -t = nil -ds18b20 = nil -package.loaded["ds18b20"]=nil -``` -####参见 -**-** []() - diff --git a/lua_modules/ds18b20/ds18b20.lua b/lua_modules/ds18b20/ds18b20.lua index de2d869b..7571ad39 100644 --- a/lua_modules/ds18b20/ds18b20.lua +++ b/lua_modules/ds18b20/ds18b20.lua @@ -1,143 +1,119 @@ -------------------------------------------------------------------------------- -- DS18B20 one wire module for NODEMCU --- NODEMCU TEAM --- LICENCE: http://opensource.org/licenses/MIT --- Vowstar --- 2015/02/14 sza2 Fix for negative values +-- by @voborsky, @devsaurus +-- encoder module is needed only for debug output; lines can be removed if no +-- debug output is needed and/or encoder module is missing +-- +-- by default the module is for integer version, comment integer version and +-- uncomment float version part for float version -------------------------------------------------------------------------------- --- Set module name as parameter of require -local modname = ... -local M = {} -_G[modname] = M --------------------------------------------------------------------------------- --- Local used variables --------------------------------------------------------------------------------- --- DS18B20 dq pin -local pin = nil --- DS18B20 default pin -local defaultPin = 9 --------------------------------------------------------------------------------- --- Local used modules --------------------------------------------------------------------------------- --- Table module -local table = table --- String module -local string = string --- One wire module -local ow = ow --- Timer module -local tmr = tmr --- Limited to local environment -setfenv(1,M) --------------------------------------------------------------------------------- --- Implementation --------------------------------------------------------------------------------- -C = 'C' -F = 'F' -K = 'K' -function setup(dq) - pin = dq - if(pin == nil) then - pin = defaultPin - end - ow.setup(pin) -end - -function addrs() - setup(pin) - tbl = {} - ow.reset_search(pin) - repeat - addr = ow.search(pin) - if(addr ~= nil) then - table.insert(tbl, addr) +return({ + pin=3, + sens={}, + temp={}, + + conversion = function(self) + local pin = self.pin + for i,s in ipairs(self.sens) do + if s.status == 0 then + print("starting conversion:", encoder.toBase64(s.addr), s.parasite == 1 and "parasite" or " ") + ow.reset(pin) + ow.select(pin, s.addr) -- select the sensor + ow.write(pin, 0x44, 1) -- and start conversion + s.status = 1 + if s.parasite == 1 then break end -- parasite sensor blocks bus during conversion + end end - tmr.wdclr() - until (addr == nil) - ow.reset_search(pin) - return tbl -end - -function readNumber(addr, unit) - result = nil - setup(pin) - flag = false - if(addr == nil) then + tmr.alarm(tmr.create(), 750, tmr.ALARM_SINGLE, function() self:readout() end) + end, + + readTemp = function(self, cb, lpin) + local pin = self.pin + self.cb = cb + self.temp={} + if lpin then pin = lpin end + ow.setup(pin) + + self.sens={} ow.reset_search(pin) - count = 0 - repeat - count = count + 1 + -- ow.target_search(pin,0x28) + -- search the first device + local addr = ow.search(pin) + -- and loop through all devices + while addr do + -- search next device + local crc=ow.crc8(string.sub(addr,1,7)) + if (crc==addr:byte(8)) and ((addr:byte(1)==0x10) or (addr:byte(1)==0x28)) then + ow.reset(pin) + ow.select(pin, addr) -- select the found sensor + ow.write(pin, 0xB4, 1) -- Read Power Supply [B4h] + local parasite = (ow.read(pin)==0 and 1 or 0) + table.insert(self.sens,{addr=addr, parasite=parasite, status=0}) + print("contact: ", encoder.toBase64(addr), parasite == 1 and "parasite" or " ") + end + addr = ow.search(pin) tmr.wdclr() - until((addr ~= nil) or (count > 100)) - ow.reset_search(pin) - end - if(addr == nil) then - return result - end - crc = ow.crc8(string.sub(addr,1,7)) - if (crc == addr:byte(8)) then - if ((addr:byte(1) == 0x10) or (addr:byte(1) == 0x28)) then - -- print("Device is a DS18S20 family device.") - ow.reset(pin) - ow.select(pin, addr) - ow.write(pin, 0x44, 1) - -- tmr.delay(1000000) - present = ow.reset(pin) - ow.select(pin, addr) - ow.write(pin,0xBE,1) - -- print("P="..present) - data = nil - data = string.char(ow.read(pin)) - for i = 1, 8 do - data = data .. string.char(ow.read(pin)) - end - -- print(data:byte(1,9)) - crc = ow.crc8(string.sub(data,1,8)) - -- print("CRC="..crc) - if (crc == data:byte(9)) then - t = (data:byte(1) + data:byte(2) * 256) - if (t > 32767) then - t = t - 65536 - end - - if (addr:byte(1) == 0x28) then - t = t * 625 -- DS18B20, 4 fractional bits - else - t = t * 5000 -- DS18S20, 1 fractional bit - end - - if(unit == nil or unit == 'C') then - -- do nothing - elseif(unit == 'F') then - t = t * 1.8 + 320000 - elseif(unit == 'K') then - t = t + 2731500 - else - return nil - end - t = t / 10000 - return t - end - tmr.wdclr() - else - -- print("Device family is not recognized.") end - else - -- print("CRC is not valid!") - end - return result -end + + -- place powered sensors first + table.sort(self.sens, function(a,b) return a.parasite 0x7fff) then t = t - 0x10000 end + if (s.addr:byte(1) == 0x28) then + t = t * 625 -- DS18B20, 4 fractional bits + else + t = t * 5000 -- DS18S20, 1 fractional bit + end + + -- integer version + 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 (tH~=85) then + self.temp[s.addr]=(sgn<0 and "-" or "")..tH.."."..tL + print(encoder.toBase64(s.addr),(sgn<0 and "-" or "")..tH.."."..tL) + s.status = 2 + end + -- end integer version + -- -- float version + -- if t and (math.floor(t/10000)~=85) then + -- self.temp[s.addr]=t + -- print(encoder.toBase64(s.addr), t) + -- s.status = 2 + -- end + -- -- end float version + end + next = next or s.status == 0 + end + if next then + node.task.post(node.task.MEDIUM_PRIORITY, function() self:conversion() end) + else + self.sens = nil + if self.cb then + node.task.post(node.task.MEDIUM_PRIORITY, function() self.cb(self.temp) end) + end + end + end -end +}) --- Return module table -return M