Commit c8ac5cfb authored by Arnim Läuger's avatar Arnim Läuger Committed by GitHub
Browse files

Merge pull request #1980 from nodemcu/dev

2.1.0 master drop
parents 22e1adc4 787379f0
As with [flashing](flash.md) there are several ways to upload code from your computer to the device. As with [flashing](flash.md) there are several ways to upload code from your computer to the device.
Note that the NodeMCU serial interface uses 115'200bps at boot time. To change the speed after booting, issue `uart.setup(0,9600,8,0,1,1)`. ESPlorer will do this automatically when changing the speed in the dropdown list. If the device panics and resets at any time, errors will be written to the serial interface at 115'200 bps. !!! note
The NodeMCU serial interface uses 115'200bps at boot time. To change the speed after booting, issue `uart.setup(0,9600,8,0,1,1)`. If the device panics and resets at any time, errors will be written to the serial interface at 115'200 bps.
# Tools # Tools
Transferring application code to ESP8266/8285 is an essential task, one that you'll perform quite frequently. Hence, it does make sense to try a few different uploading tools until you find one you feel comfortable with. [https://frightanic.com/iot/tools-ides-nodemcu/](https://frightanic.com/iot/tools-ides-nodemcu/) lists almost a dozen classical uploaders - in addition to IDEs or IDE-like applications which of course transfer code as well.
The NodeMCU firmware team does not give any recommendations as for which uploader to use nor are there any "NodeMCU approved" tools. The below listed tools are just three, in no particular order, which seem popular and/or reasonably well maintained.
## ESPlorer ## ESPlorer
...@@ -12,7 +17,7 @@ Note that the NodeMCU serial interface uses 115'200bps at boot time. To change t ...@@ -12,7 +17,7 @@ Note that the NodeMCU serial interface uses 115'200bps at boot time. To change t
Source: [https://github.com/4refr0nt/ESPlorer](https://github.com/4refr0nt/ESPlorer) Source: [https://github.com/4refr0nt/ESPlorer](https://github.com/4refr0nt/ESPlorer)
Supported platforms: OS X, Linux, Windows, anything that runs Java Supported platforms: macOS, Linux, Windows, anything that runs Java
## nodemcu-uploader.py ## nodemcu-uploader.py
...@@ -20,23 +25,16 @@ Supported platforms: OS X, Linux, Windows, anything that runs Java ...@@ -20,23 +25,16 @@ Supported platforms: OS X, Linux, Windows, anything that runs Java
Source: [https://github.com/kmpm/nodemcu-uploader](https://github.com/kmpm/nodemcu-uploader) Source: [https://github.com/kmpm/nodemcu-uploader](https://github.com/kmpm/nodemcu-uploader)
Supported platforms: OS X, Linux, Windows, anything that runs Python Supported platforms: macOS, Linux, Windows, anything that runs Python
## NodeMCU Studio
> THIS TOOL IS IN REALLY REALLY REALLY REALLY EARLY STAGE!!!!!!!!!!!!!!!!!!!!!!!!!!!
Source: [https://github.com/nodemcu/nodemcu-studio-csharp](https://github.com/nodemcu/nodemcu-studio-csharp) ## NodeMCU-Tool
Supported platforms: Windows > Upload/Download Lua files to your ESP8266 module with NodeMCU firmware.
> Simple. Command Line. Cross-Platform. File Management. NodeMCU.
## luatool Source: [https://github.com/andidittrich/NodeMCU-Tool](https://github.com/andidittrich/NodeMCU-Tool)
> Allow easy uploading of any Lua-based script into the ESP8266 flash memory with NodeMcu firmware Supported platforms: macOS, Linux Windows, anything that runs Node.js
Source: [https://github.com/4refr0nt/luatool](https://github.com/4refr0nt/luatool)
Supported platforms: OS X, Linux, Windows, anything that runs Python
# init.lua # init.lua
You will see "lua: cannot open init.lua" printed to the serial console when the device boots after it's been freshly flashed. If NodeMCU finds a `init.lua` in the root of the file system it will execute it as part of the boot sequence (standard Lua feature). Hence, your application is initialized and triggered from `init.lua`. Usually you first set up the WiFi connection and only continue once that has been successful. You will see "lua: cannot open init.lua" printed to the serial console when the device boots after it's been freshly flashed. If NodeMCU finds a `init.lua` in the root of the file system it will execute it as part of the boot sequence (standard Lua feature). Hence, your application is initialized and triggered from `init.lua`. Usually you first set up the WiFi connection and only continue once that has been successful.
...@@ -58,24 +56,65 @@ function startup() ...@@ -58,24 +56,65 @@ function startup()
end end
end end
print("Connecting to WiFi access point...") -- Define WiFi station event callbacks
wifi.setmode(wifi.STATION) wifi_connect_event = function(T)
wifi.sta.config(SSID, PASSWORD) print("Connection to AP("..T.SSID..") established!")
-- wifi.sta.connect() not necessary because config() uses auto-connect=true by default
tmr.create():alarm(1000, tmr.ALARM_AUTO, function(cb_timer)
if wifi.sta.getip() == nil then
print("Waiting for IP address...") print("Waiting for IP address...")
else if disconnect_ct ~= nil then disconnect_ct = nil end
cb_timer:unregister() end
print("WiFi connection established, IP address: " .. wifi.sta.getip())
print("You have 3 seconds to abort") wifi_got_ip_event = function(T)
-- Note: Having an IP address does not mean there is internet access!
-- Internet connectivity can be determined with net.dns.resolve().
print("Wifi connection is ready! IP address is: "..T.IP)
print("Startup will resume momentarily, you have 3 seconds to abort.")
print("Waiting...") print("Waiting...")
tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup) tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup)
end
wifi_disconnect_event = function(T)
if T.reason == wifi.eventmon.reason.ASSOC_LEAVE then
--the station has disassociated from a previously connected AP
return
end end
end) -- total_tries: how many times the station will attempt to connect to the AP. Should consider AP reboot duration.
``` local total_tries = 75
print("\nWiFi connection to AP("..T.SSID..") has failed!")
--There are many possible disconnect reasons, the following iterates through
--the list and returns the string corresponding to the disconnect reason.
for key,val in pairs(wifi.eventmon.reason) do
if val == T.reason then
print("Disconnect reason: "..val.."("..key..")")
break
end
end
if disconnect_ct == nil then
disconnect_ct = 1
else
disconnect_ct = disconnect_ct + 1
end
if disconnect_ct < total_tries then
print("Retrying connection...(attempt "..(disconnect_ct+1).." of "..total_tries..")")
else
wifi.sta.disconnect()
print("Aborting connection to AP!")
disconnect_ct = nil
end
end
-- Register WiFi Station event callbacks
wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, wifi_connect_event)
wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, wifi_got_ip_event)
wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, wifi_disconnect_event)
Inspired by [https://github.com/ckuehnel/NodeMCU-applications](https://github.com/ckuehnel/NodeMCU-applications) print("Connecting to WiFi access point...")
wifi.setmode(wifi.STATION)
wifi.sta.config({ssid=SSID, pwd=PASSWORD, save=true})
-- wifi.sta.connect() not necessary because config() uses auto-connect=true by default
```
# Compiling Lua on your PC for Uploading # Compiling Lua on your PC for Uploading
......
...@@ -38,7 +38,8 @@ var nodemcu = nodemcu || {}; ...@@ -38,7 +38,8 @@ var nodemcu = nodemcu || {};
} }
function createTocTableRow(func, intro) { function createTocTableRow(func, intro) {
// fragile attempt to auto-create the in-page anchor // fragile attempt to auto-create the in-page anchor
var href = func.replace(/\.|:/g, '').replace('()', '').replace(' --', '-').replace(/ /g, '-'); // good tests: file.md,
var href = func.replace(/[\.:\(\)]/g, '').replace(/ --|, | /g, '-');
var link = '<a href="#' + href.toLowerCase() + '">' + func + '</a>'; var link = '<a href="#' + href.toLowerCase() + '">' + func + '</a>';
return '<tr><td>' + link + '</td><td>' + intro + '</td></tr>'; return '<tr><td>' + link + '</td><td>' + intro + '</td></tr>';
} }
...@@ -180,7 +181,7 @@ var nodemcu = nodemcu || {}; ...@@ -180,7 +181,7 @@ var nodemcu = nodemcu || {};
// path is like /en/<branch>/<lang>/build/ -> extract 'lang' // path is like /en/<branch>/<lang>/build/ -> extract 'lang'
// split[0] is an '' because the path starts with the separator // split[0] is an '' because the path starts with the separator
selectedLanguageCode = path.split('/')[3]; selectedLanguageCode = path.split('/')[3];
} else { } else if (!window.location.href.startsWith('file://')) {
// path is like /<lang>/build/ -> extract 'lang' // path is like /<lang>/build/ -> extract 'lang'
selectedLanguageCode = path.substr(1, 2); selectedLanguageCode = path.substr(1, 2);
} }
......
...@@ -5,7 +5,7 @@ MEMORY ...@@ -5,7 +5,7 @@ MEMORY
dport0_0_seg : org = 0x3FF00000, len = 0x10 dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000 iram1_0_seg : org = 0x40100000, len = 0x8000
irom0_0_seg : org = 0x40210000, len = 0xD0000 irom0_0_seg : org = 0x40210000, len = 0xE0000
} }
PHDRS PHDRS
...@@ -105,7 +105,7 @@ SECTIONS ...@@ -105,7 +105,7 @@ SECTIONS
/* SDK libraries that used in bootup process, interruption handling /* SDK libraries that used in bootup process, interruption handling
* and other ways where flash cache (iROM) is unavailable: */ * and other ways where flash cache (iROM) is unavailable: */
*libmain.a:*(.literal .text) *libmain.a:*(.literal .literal.* .text .text.*)
*libnet80211.a:*(.literal .text) *libnet80211.a:*(.literal .text)
*libphy.a:*(.literal .text) *libphy.a:*(.literal .text)
*libpp.a:*(.literal .text) *libpp.a:*(.literal .text)
......
...@@ -18,7 +18,7 @@ end ...@@ -18,7 +18,7 @@ end
-- payload(json): {"cmd":xxx,"content":xxx} -- payload(json): {"cmd":xxx,"content":xxx}
function topic1func(m,pl) function topic1func(m,pl)
print("get1: "..pl) print("get1: "..pl)
local pack = cjson.decode(pl) local pack = sjson.decode(pl)
if pack.content then if pack.content then
if pack.cmd == "open" then file.open(pack.content,"w+") if pack.cmd == "open" then file.open(pack.content,"w+")
elseif pack.cmd == "write" then file.write(pack.content) elseif pack.cmd == "write" then file.write(pack.content)
......
--'
-- ds18b20 one wire example for NODEMCU (Integer firmware only)
-- NODEMCU TEAM
-- LICENCE: http://opensource.org/licenses/MIT
-- Vowstar <vowstar@nodemcu.com>
--'
pin = 9
ow.setup(pin)
count = 0
repeat
count = count + 1
addr = ow.reset_search(pin)
addr = ow.search(pin)
tmr.wdclr()
until((addr ~= nil) or (count > 100))
if (addr == nil) then
print("No more addresses.")
else
print(addr:byte(1,8))
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.")
repeat
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)
-- handle negative temperatures
if (t > 0x7fff) then
t = t - 0x10000
end
if (addr:byte(1) == 0x28) then
t = t * 625 -- DS18B20, 4 fractional bits
else
t = t * 5000 -- DS18S20, 1 fractional bit
end
local sign = ""
if (t < 0) then
sign = "-"
t = -1 * t
end
-- Separate integral and decimal portions, for integer firmware only
local t1 = string.format("%d", t / 10000)
local t2 = string.format("%04u", t % 10000)
local temp = sign .. t1 .. "." .. t2
print("Temperature= " .. temp .. " Celsius")
end
tmr.wdclr()
until false
else
print("Device family is not recognized.")
end
else
print("CRC is not valid!")
end
end
-- Somfy module example (beside somfy module requires also CJSON module) -- Somfy module example (beside somfy module requires also SJSON module)
-- The rolling code number is stored in the file somfy.cfg. A cached write of the somfy.cfg file is implemented in order to reduce the number of write to the EEPROM memory. Together with the logic of the file module it should allow long lasting operation. -- The rolling code number is stored in the file somfy.cfg. A cached write of the somfy.cfg file is implemented in order to reduce the number of write to the EEPROM memory. Together with the logic of the file module it should allow long lasting operation.
config_file = "somfy." config_file = "somfy."
...@@ -43,7 +43,7 @@ function readconfig() ...@@ -43,7 +43,7 @@ function readconfig()
end end
if not ln then ln = "{}" end if not ln then ln = "{}" end
print("Configuration: "..ln) print("Configuration: "..ln)
config = cjson.decode(ln) config = sjson.decode(ln)
config_saved = deepcopy(config) config_saved = deepcopy(config)
end end
...@@ -52,7 +52,7 @@ function writeconfighard() ...@@ -52,7 +52,7 @@ function writeconfighard()
file.remove(config_file.."bak") file.remove(config_file.."bak")
file.rename(config_file.."cfg", config_file.."bak") file.rename(config_file.."cfg", config_file.."bak")
file.open(config_file.."cfg", "w+") file.open(config_file.."cfg", "w+")
local ok, cfg = pcall(cjson.encode, config) local ok, cfg = pcall(sjson.encode, config)
if ok then if ok then
file.writeline(cfg) file.writeline(cfg)
else else
...@@ -68,8 +68,8 @@ function writeconfig() ...@@ -68,8 +68,8 @@ function writeconfig()
local savenow = false local savenow = false
local savelater = false local savelater = false
--print("Config: "..cjson.encode(config)) --print("Config: "..sjson.encode(config))
--print("Config saved: "..cjson.encode(config)) --print("Config saved: "..sjson.encode(config))
local count = 0 local count = 0
for _ in pairs(config_saved) do count = count + 1 end for _ in pairs(config_saved) do count = count + 1 end
...@@ -134,7 +134,7 @@ end ...@@ -134,7 +134,7 @@ end
--======================================================================================================-- --======================================================================================================--
if not config then readconfig() end if not config then readconfig() end
if #config == 0 then -- somfy.cfg does not exist if #config == 0 then -- somfy.cfg does not exist
config = cjson.decode([[{"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}}]]) config = sjson.decode([[{"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}}]])
config_saved = deepcopy(config) config_saved = deepcopy(config)
end end
down('window1', down('window1',
......
# tz module
This is a simple module that parses timezone files as found on unix systems. It is oriented around converting the current time. It can convert other times, but it is
rather less efficient as it maintains only a single cached entry in memory.
On my linux system, these files can be found in `/usr/share/zoneinfo`.
## tz.setzone()
This sets the timezone to be used in subsequent conversions
#### Syntax
`tz.setzone(timezone)`
#### Parameters
- `timezone` this is the timezone string. It must correspond to a file in the file system which is named timezone.zone.
#### Returns
true if the zone exists in the file system.
## tz.getoffset()
This gets the offset (in seconds) of the time passed as the argument.
#### Syntax
`tz.getoffset(time)`
#### Parameters
- `time` the number of seconds since the epoch. This is the same value as used by the `sntp` module.
#### Returns
- The number of seconds of offset. West of Greenwich is negative.
- The start time (in epoch seconds) of this offset.
- The end time (in epoch seconds) of this offset.
#### Example
```
tz = require('tz')
tz.setzone('eastern')
sntp.sync(nil, function(now)
local tm = rtctime.epoch2cal(now + tz.getoffset(now))
print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]))
end)
```
## tz.getzones()
This returns a list of the available timezones in the file system.
#### Syntax
`tz.getzones()`
#### Returns
A list of timezones.
-- tz -- A simple timezone module for interpreting zone files
local M = {}
local tstart = 0
local tend = 0
local toffset = 0
local thezone = "eastern"
function M.setzone(zone)
thezone = zone
return M.exists(thezone)
end
function M.exists(zone)
return file.exists(zone .. ".zone")
end
function M.getzones()
local result = {}
for fn, _ in pairs(file.list()) do
local _, _, prefix = string.find(fn, "(.*).zone")
if prefix then
table.insert(result, prefix)
end
end
return result
end
function load(t)
local z = file.open(thezone .. ".zone", "r")
local hdr = z:read(20)
local magic = struct.unpack("c4 B", hdr)
if magic == "TZif" then
local lens = z:read(24)
local ttisgmt_count, ttisdstcnt, leapcnt, timecnt, typecnt, charcnt = struct.unpack("> LLLLLL", lens)
local times = z:read(4 * timecnt)
local typeindex = z:read(timecnt)
local ttinfos = z:read(6 * typecnt)
z:close()
local offset = 1
local tt
for i = 1, timecnt do
tt = struct.unpack(">l", times, (i - 1) * 4 + 1)
if t < tt then
offset = (i - 2)
tend = tt
break
end
tstart = tt
end
local tindex = struct.unpack("B", typeindex, offset + 1)
toffset = struct.unpack(">l", ttinfos, tindex * 6 + 1)
else
tend = 0x7fffffff
tstart = 0
end
end
function M.getoffset(t)
if t < tstart or t >= tend then
-- Ignore errors
local ok, msg = pcall(function ()
load(t)
end)
if not ok then
print (msg)
end
end
return toffset, tstart, tend
end
return M
------------------------------------------------------------------------------
-- DS18B20 query module
--
-- LICENCE: http://opensource.org/licenses/MIT
-- Vladimir Dronnikov <dronnikov@gmail.com>
--
-- Example:
-- dofile("ds18b20.lua").read(4, function(r) for k, v in pairs(r) do print(k, v) end end)
------------------------------------------------------------------------------
local M
do
local bit = bit
local format_addr = function(a)
return ("%02x-%02x%02x%02x%02x%02x%02x"):format(
a:byte(1),
a:byte(7), a:byte(6), a:byte(5),
a:byte(4), a:byte(3), a:byte(2)
)
end
local read = function(pin, cb, delay)
local ow = require("ow")
-- get list of relevant devices
local d = { }
ow.setup(pin)
ow.reset_search(pin)
while true do
tmr.wdclr()
local a = ow.search(pin)
if not a then break end
if ow.crc8(a) == 0 and
(a:byte(1) == 0x10 or a:byte(1) == 0x28)
then
d[#d + 1] = a
end
end
-- conversion command for all
ow.reset(pin)
ow.skip(pin)
ow.write(pin, 0x44, 1)
-- wait a bit
tmr.alarm(0, delay or 100, 0, function()
-- iterate over devices
local r = { }
for i = 1, #d do
tmr.wdclr()
-- read rom command
ow.reset(pin)
ow.select(pin, d[i])
ow.write(pin, 0xBE, 1)
-- read data
local x = ow.read_bytes(pin, 9)
if ow.crc8(x) == 0 then
local t = (x:byte(1) + x:byte(2) * 256)
-- negatives?
if bit.isset(t, 15) then t = 1 - bit.bxor(t, 0xffff) end
-- NB: temperature in Celsius * 10^4
t = t * 625
-- NB: due 850000 means bad pullup. ignore
if t ~= 850000 then
r[format_addr(d[i])] = t
end
d[i] = nil
end
end
cb(r)
end)
end
-- expose
M = {
read = read,
}
end
return M
# DS18B20 Module
## Require
```lua
ds18b20 = require("ds18b20")
```
## Release
```lua
ds18b20 = nil
package.loaded["ds18b20"]=nil
```
<a id="ds18b20_setup"></a>
## 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. Parasite-powered sensors are read one by one. The first parasite-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.toHex()`).
#### Syntax
`readTemp(callback, pin)`
#### Parameters
- `callback` function that receives all results when all conversions finish. The callback function 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
t = require("ds18b20")
pin = 3 -- gpio0 = 3, gpio2 = 4
function readout(temp)
for addr, temp in pairs(temp) do
print(string.format("Sensor %s: %s 'C", encoder.toHex(addr), temp))
end
-- 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.toHex(s.addr), s.parasite == 1 and " (parasite)" or "")) -- readable address with Hex encoding is preferred when encoder module is available
end
end
```
-- 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") t = require("ds18b20")
pin = 3 -- gpio0 = 3, gpio2 = 4
-- ESP-01 GPIO Mapping function readout(temp)
gpio0 = 3 for addr, temp in pairs(temp) do
gpio2 = 4 -- print(string.format("Sensor %s: %s 'C", addr, temp))
print(string.format("Sensor %s: %s °C", encoder.toHex(addr), temp)) -- readable address with base64 encoding is preferred when encoder module is available
end
t.setup(gpio0) -- Module can be released when it is no longer needed
addrs = t.addrs() t = nil
if (addrs ~= nil) then package.loaded["ds18b20"]=nil
print("Total DS18B20 sensors: "..table.getn(addrs))
end end
-- Just read temperature -- t:readTemp(readout) -- default pin value is 3
print("Temperature: "..t.read().."'C") t:readTemp(readout, pin)
if t.sens then
-- Get temperature of first detected sensor in Fahrenheit print("Total number of DS18B20 sensors: "..table.getn(t.sens))
print("Temperature: "..t.read(nil,t.F).."'F") 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 ""))
-- Query the second detected sensor, get temperature in Kelvin print(string.format(" sensor #%d address: %s%s", i, encoder.toHex(s.addr), s.parasite == 1 and " (parasite)" or "")) -- readable address with base64 encoding is preferred when encoder module is available
if (table.getn(addrs) >= 2) then end
print("Second sensor: "..t.read(addrs[2],t.K).."'K")
end end
-- Don't forget to release it after use
t = nil
ds18b20 = nil
package.loaded["ds18b20"]=nil
require('ds18b20') t = require('ds18b20')
port = 80 port = 80
pin = 3 -- gpio0 = 3, gpio2 = 4
gconn = {} -- global variable for connection
-- ESP-01 GPIO Mapping function readout(temp)
gpio0, gpio2 = 3, 4 local resp = "HTTP/1.1 200 OK\nContent-Type: text/html\nRefresh: 5\n\n" ..
"<!DOCTYPE HTML>" ..
"<html><body>" ..
"<b>ESP8266</b></br>"
for addr, temp in pairs(temp) do
-- resp = resp .. string.format("Sensor %s: %s &#8451</br>", addr, temp)
resp = resp .. string.format("Sensor %s: %s &#8451</br>", encoder.toHex(addr), temp) -- readable address with base64 encoding is preferred when encoder module is available
end
resp = resp ..
"Node ChipID: " .. node.chipid() .. "<br>" ..
"Node MAC: " .. wifi.sta.getmac() .. "<br>" ..
"Node Heap: " .. node.heap() .. "<br>" ..
"Timer Ticks: " .. tmr.now() .. "<br>" ..
"</html></body>"
ds18b20.setup(gpio0) gconn:send(resp)
gconn:on("sent",function(conn) conn:close() end)
end
srv=net.createServer(net.TCP) srv=net.createServer(net.TCP)
srv:listen(port, srv:listen(port,
function(conn) function(conn)
conn:send("HTTP/1.1 200 OK\nContent-Type: text/html\nRefresh: 5\n\n" .. gconn = conn
"<!DOCTYPE HTML>" .. -- t:readTemp(readout) -- default pin value is 3
"<html><body>" .. t:readTemp(readout, pin)
"<b>ESP8266</b></br>" ..
"Temperature : " .. ds18b20.read() .. "<br>" ..
"Node ChipID : " .. node.chipid() .. "<br>" ..
"Node MAC : " .. wifi.sta.getmac() .. "<br>" ..
"Node Heap : " .. node.heap() .. "<br>" ..
"Timer Ticks : " .. tmr.now() .. "<br>" ..
"</html></body>")
conn:on("sent",function(conn) conn:close() end)
end end
) )
#DS18B20 Module
##Require
```lua
ds18b20 = require("ds18b20")
```
## Release
```lua
ds18b20 = nil
package.loaded["ds18b20"]=nil
```
##Constant
C, F, K
<a id="ds18b20_setup"></a>
##setup()
####Description
Setting the pin of DS18B20.<br />
####Syntax
setup(pin)
####Parameters
pin: 1~10, IO index. If parameter is nil, it will use pin 9(GPIO2) automatically.<br />
####Returns
nil
####Example
```lua
ds18b20 = require("ds18b20")
ds18b20.setup(9)
-- Don't forget to release it after use
ds18b20 = nil
package.loaded["ds18b20"]=nil
```
####See also
**-** []()
<a id="ds18b20_addrs"></a>
## 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. <br />
####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. <br />
####Example
```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
```
####See also
**-** []()
<a id="ds18b20_readNumber"></a>
## 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. <br />
####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.<br />
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. <br />
####Returns
t1: integer. The integer part of the temperature. If it read fails, return nil. <br />
t2: integer. The fractional part of the temperature. If it read fails, return nil. <br />
####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
**-** []()
<a id="ds18b20_read"></a>
## 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. <br />
####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.<br />
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. <br />
####Returns
t: string. The string of the temperature. If it read fails, return nil.<br />
####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
**-** []()
#DS18B20 模块
##引用
```lua
ds18b20 = require("ds18b20")
```
#释放
```lua
ds18b20 = nil
package.loaded["ds18b20"]=nil
```
##常量
C, F, K
<a id="ds18b20_setup"></a>
##setup()
####描述
设置DS18B20所在的管脚(pin)。<br />
####语法
setup(pin)
####参数
pin: 1~10, IO 编号。如果参数为nil,会自动设定为9(GPIO2).<br />
####返回值
nil
####示例
```lua
ds18b20 = require("ds18b20")
ds18b20.setup(9)
-- Don't forget to release it after use
ds18b20 = nil
package.loaded["ds18b20"]=nil
```
####参见
**-** []()
<a id="ds18b20_addrs"></a>
## addrs()
####描述
返回单总线上所有DS18B20器件的地址列表(table)。如果没有执行过setup(pin),则会自动对引脚9(GPIO2)进行单总线模式初始化。<br />
####语法
addrs()
####参数
nil
####返回值
addrs: 返回包含单总线上所有DS18B20器件的地址列表(table)。其中地址是字符串类型(String)。如果失败则返回nil. <br />
####示例
```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
```
####参见
**-** []()
<a id="ds18b20_readNumber"></a>
## readNumber()
####描述
读取温度数值。如果没有执行过setup(pin),则会自动对引脚9(GPIO2)进行单总线模式初始化。 <br />
####语法
readNumber(addr, unit)
####参数
addr: 字符串, DS18B20地址。 如果该参数为nil,会自动选择第一个发现的地址。<br />
unit: 单位转换,只接受常量C(摄氏度),F(华氏度), K(开氏度)。如果该参数为nil,会自动选择常量C(摄氏度) 。<br />
####返回值
t1: 数值,温度的整数部分。如果读取失败返回nil.<br />
t2: 数值,温度的小数部分。如果读取失败返回nil.<br />
####示例
```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
```
####参见
**-** []()
<a id="ds18b20_read"></a>
## read()
####描述
读取温度字符串。如果没有执行过setup(pin),则会自动对引脚9(GPIO2)进行单总线模式初始化。 <br />
####语法
read(addr, unit)
####参数
addr: 字符串, DS18B20地址。 如果该参数为nil,会自动选择第一个发现的地址。<br />
unit: 单位转换,只接受常量C(摄氏度),F(华氏度), K(开氏度)。如果该参数为nil,会自动选择常量C(摄氏度) 。<br />
####返回值
t: 字符串,表示成字符串形式的温度。如果读取失败返回nil.<br />
####示例
```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
```
####参见
**-** []()
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- DS18B20 one wire module for NODEMCU -- DS18B20 one wire module for NODEMCU
-- NODEMCU TEAM -- by @voborsky, @devsaurus
-- LICENCE: http://opensource.org/licenses/MIT -- encoder module is needed only for debug output; lines can be removed if no
-- Vowstar <vowstar@nodemcu.com> -- debug output is needed and/or encoder module is missing
-- 2015/02/14 sza2 <sza2trash@gmail.com> Fix for negative values --
-- 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 return({
local modname = ... pin=3,
local M = {} sens={},
_G[modname] = M temp={},
--------------------------------------------------------------------------------
-- Local used variables conversion = function(self)
-------------------------------------------------------------------------------- local pin = self.pin
-- DS18B20 dq pin for i,s in ipairs(self.sens) do
local pin = nil if s.status == 0 then
-- DS18B20 default pin print("starting conversion:", encoder.toHex(s.addr), s.parasite == 1 and "parasite" or " ")
local defaultPin = 9 ow.reset(pin)
-------------------------------------------------------------------------------- ow.select(pin, s.addr) -- select the sensor
-- Local used modules ow.write(pin, 0x44, 1) -- and start conversion
-------------------------------------------------------------------------------- s.status = 1
-- Table module if s.parasite == 1 then break end -- parasite sensor blocks bus during conversion
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 end
end
tmr.create():alarm(750, tmr.ALARM_SINGLE, function() self:readout() end)
end,
readTemp = function(self, cb, lpin)
if lpin then self.pin = lpin end
local pin = self.pin
self.cb = cb
self.temp={}
ow.setup(pin) ow.setup(pin)
end
function addrs() self.sens={}
setup(pin)
tbl = {}
ow.reset_search(pin) ow.reset_search(pin)
repeat -- ow.target_search(pin,0x28)
addr = ow.search(pin) -- search the first device
if(addr ~= nil) then local addr = ow.search(pin)
table.insert(tbl, addr) -- 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.toHex(addr), parasite == 1 and "parasite" or " ")
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
ow.reset_search(pin)
count = 0
repeat
count = count + 1
addr = ow.search(pin) addr = ow.search(pin)
tmr.wdclr() tmr.wdclr()
until((addr ~= nil) or (count > 100))
ow.reset_search(pin)
end
if(addr == nil) then
return result
end end
crc = ow.crc8(string.sub(addr,1,7))
if (crc == addr:byte(8)) then -- place powered sensors first
if ((addr:byte(1) == 0x10) or (addr:byte(1) == 0x28)) then table.sort(self.sens, function(a,b) return a.parasite<b.parasite end)
-- print("Device is a DS18S20 family device.")
node.task.post(node.task.MEDIUM_PRIORITY, function() self:conversion() end)
end,
readout=function(self)
local pin = self.pin
local next = false
if not self.sens then return 0 end
for i,s in ipairs(self.sens) do
-- print(encoder.toHex(s.addr), s.status)
if s.status == 1 then
ow.reset(pin) ow.reset(pin)
ow.select(pin, addr) ow.select(pin, s.addr) -- select the sensor
ow.write(pin, 0x44, 1) ow.write(pin, 0xBE, 0) -- READ_SCRATCHPAD
-- tmr.delay(1000000) data = ow.read_bytes(pin, 9)
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 local t=(data:byte(1)+data:byte(2)*256)
if (t > 0x7fff) then t = t - 0x10000 end
if (s.addr:byte(1) == 0x28) then
t = t * 625 -- DS18B20, 4 fractional bits t = t * 625 -- DS18B20, 4 fractional bits
else else
t = t * 5000 -- DS18S20, 1 fractional bit t = t * 5000 -- DS18S20, 1 fractional bit
end end
if(unit == nil or unit == 'C') then if 1/2 == 0 then
-- do nothing -- integer version
elseif(unit == 'F') then local sgn = t<0 and -1 or 1
t = t * 1.8 + 320000 local tA = sgn*t
elseif(unit == 'K') then local tH=tA/10000
t = t + 2731500 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.toHex(s.addr),(sgn<0 and "-" or "")..tH.."."..tL)
s.status = 2
end
-- end integer version
else else
return nil -- float version
if t and (math.floor(t/10000)~=85) then
self.temp[s.addr]=t/10000
print(encoder.toHex(s.addr), t)
s.status = 2
end end
t = t / 10000 -- end float version
return t
end end
tmr.wdclr()
else
-- print("Device family is not recognized.")
end end
else next = next or s.status == 0
-- print("CRC is not valid!")
end end
return result if next then
end node.task.post(node.task.MEDIUM_PRIORITY, function() self:conversion() end)
function read(addr, unit)
t = readNumber(addr, unit)
if (t == nil) then
return nil
else else
return t 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 end
return M })
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment