Merge pull request #2088 from nodemcu/dev

Next master drop
This commit is contained in:
Marcel Stör 2017-08-24 21:23:18 +02:00 committed by GitHub
commit 2e67ff5a63
13 changed files with 740 additions and 162 deletions

View File

@ -76,9 +76,9 @@ else
endif endif
CCFLAGS += -ffunction-sections -fno-jump-tables -fdata-sections CCFLAGS += -ffunction-sections -fno-jump-tables -fdata-sections
AR = xtensa-lx106-elf-ar AR = xtensa-lx106-elf-ar
CC = xtensa-lx106-elf-gcc CC = $(WRAPCC) xtensa-lx106-elf-gcc
NM = xtensa-lx106-elf-nm NM = xtensa-lx106-elf-nm
CPP = xtensa-lx106-elf-cpp CPP = $(WRAPCC) xtensa-lx106-elf-gcc -E
OBJCOPY = xtensa-lx106-elf-objcopy OBJCOPY = xtensa-lx106-elf-objcopy
FIRMWAREDIR = ../bin/ FIRMWAREDIR = ../bin/
UNAME_S := $(shell uname -s) UNAME_S := $(shell uname -s)

View File

@ -29,6 +29,7 @@
//#define LUA_USE_MODULES_CRON //#define LUA_USE_MODULES_CRON
//#define LUA_USE_MODULES_CRYPTO //#define LUA_USE_MODULES_CRYPTO
#define LUA_USE_MODULES_DHT #define LUA_USE_MODULES_DHT
//#define LUA_USE_MODULES_DS18B20
//#define LUA_USE_MODULES_ENCODER //#define LUA_USE_MODULES_ENCODER
//#define LUA_USE_MODULES_ENDUSER_SETUP // USE_DNS in dhcpserver.h needs to be enabled for this module to work. //#define LUA_USE_MODULES_ENDUSER_SETUP // USE_DNS in dhcpserver.h needs to be enabled for this module to work.
#define LUA_USE_MODULES_FILE #define LUA_USE_MODULES_FILE

View File

@ -120,6 +120,10 @@ reswitch: switch (ch = *fmt++) {
va_arg(ap, uint32_t) : va_arg(ap, uint32_t); va_arg(ap, uint32_t) : va_arg(ap, uint32_t);
kprintn(put, ul, 10, width, zwidth); kprintn(put, ul, 10, width, zwidth);
break; break;
case 'p':
ul = va_arg(ap, ptrdiff_t);
kprintn(put, ul, 16, width, zwidth);
break;
case 'x': case 'x':
ul = lflag ? ul = lflag ?
va_arg(ap, uint32_t) : va_arg(ap, uint32_t); va_arg(ap, uint32_t) : va_arg(ap, uint32_t);

View File

@ -8,7 +8,6 @@
#include "c_string.h" #include "c_string.h"
#include "ets_sys.h" #include "ets_sys.h"
#include "time.h" #include "time.h"
#include "rtc/rtctime_internal.h"
#include "rtc/rtctime.h" #include "rtc/rtctime.h"
#include "stdlib.h" #include "stdlib.h"
#include "mem.h" #include "mem.h"
@ -189,7 +188,7 @@ static void cron_handle_time(uint8_t mon, uint8_t dom, uint8_t dow, uint8_t hour
static void cron_handle_tmr() { static void cron_handle_tmr() {
struct rtc_timeval tv; struct rtc_timeval tv;
rtc_time_gettimeofday(&tv); rtctime_gettimeofday(&tv);
if (tv.tv_sec == 0) { // Wait for RTC time if (tv.tv_sec == 0) { // Wait for RTC time
ets_timer_arm_new(&cron_timer, 1000, 0, 1); ets_timer_arm_new(&cron_timer, 1000, 0, 1);
return; return;

295
app/modules/ds18b20.c Normal file
View File

@ -0,0 +1,295 @@
//***************************************************************************
// DS18B20 module for ESP8266 with nodeMCU
// fetchbot @github
// MIT license, http://opensource.org/licenses/MIT
//***************************************************************************
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "osapi.h"
#include "driver/onewire.h"
#include "c_stdio.h"
#include "c_stdlib.h"
//***************************************************************************
// OW ROM COMMANDS
//***************************************************************************
#define DS18B20_ROM_SEARCH (0xF0)
#define DS18B20_ROM_READ (0x33)
#define DS18B20_ROM_MATCH (0x55)
#define DS18B20_ROM_SKIP (0xCC)
#define DS18B20_ROM_SEARCH_ALARM (0xEC)
//***************************************************************************
// OW FUNCTION COMMANDS
//***************************************************************************
#define DS18B20_FUNC_CONVERT (0x44)
#define DS18B20_FUNC_SCRATCH_WRITE (0x4E)
#define DS18B20_FUNC_SCRATCH_READ (0xBE)
#define DS18B20_FUNC_SCRATCH_COPY (0x48)
#define DS18B20_FUNC_E2_RECALL (0xB8)
#define DS18B20_FUNC_POWER_READ (0xB4)
//***************************************************************************
// Initial EEPROM values
//***************************************************************************
#define DS18B20_EEPROM_TH (0x4B) // 75 degree
#define DS18B20_EEPROM_TL (0x46) // 70 degree
#define DS18B20_EEPROM_RES (0x7F) // 12 bit resolution
//***************************************************************************
static uint8_t ds18b20_bus_pin;
static uint8_t ds18b20_device_family;
static uint8_t ds18b20_device_search = 0;
static uint8_t ds18b20_device_index;
static uint8_t ds18b20_device_par;
static uint8_t ds18b20_device_conf[3];
static uint8_t ds18b20_device_rom[8];
static uint8_t ds18b20_device_scratchpad[9];
static double ds18b20_device_scratchpad_temp;
static int ds18b20_device_scratchpad_temp_dec;
static uint8_t ds18b20_device_scratchpad_conf;
static uint8_t ds18b20_device_res = 12; // 12 bit resolution (750ms conversion time)
os_timer_t ds18b20_timer; // timer for conversion delay
int ds18b20_timer_ref; // callback when readout is ready
int ds18b20_table_ref;
static int ds18b20_table_offset;
static int ds18b20_lua_readoutdone(void);
// Setup onewire bus for DS18B20 temperature sensors
// Lua: ds18b20.setup(OW_BUS_PIN)
static int ds18b20_lua_setup(lua_State *L) {
// check ow bus pin value
if (!lua_isnumber(L, 1) || lua_isnumber(L, 1) == 0) {
return luaL_error(L, "wrong 1-wire pin");
}
ds18b20_bus_pin = luaL_checkinteger(L, 1);
MOD_CHECK_ID(ow, ds18b20_bus_pin);
onewire_init(ds18b20_bus_pin);
}
static int ds18b20_set_device(uint8_t *ds18b20_device_rom) {
onewire_reset(ds18b20_bus_pin);
onewire_select(ds18b20_bus_pin, ds18b20_device_rom);
onewire_write(ds18b20_bus_pin, DS18B20_FUNC_SCRATCH_WRITE, 0);
onewire_write_bytes(ds18b20_bus_pin, ds18b20_device_conf, 3, 0);
}
// Change sensor settings
// Lua: ds18b20.setting(ROM, RES)
static int ds18b20_lua_setting(lua_State *L) {
// check rom table and resolution setting
if (!lua_istable(L, 1) || !lua_isnumber(L, 2)) {
return luaL_error(L, "wrong arg range");
}
ds18b20_device_res = luaL_checkinteger(L, 2);
if (!((ds18b20_device_res == 9) || (ds18b20_device_res == 10) || (ds18b20_device_res == 11) || (ds18b20_device_res == 12))) {
return luaL_error(L, "Invalid argument: resolution");
}
// no change to th and tl setting
ds18b20_device_conf[0] = DS18B20_EEPROM_TH;
ds18b20_device_conf[1] = DS18B20_EEPROM_TL;
ds18b20_device_conf[2] = ((ds18b20_device_res - 9) << 5) + 0x1F;
uint8_t table_len = lua_objlen(L, 1);
const char *str[table_len];
const char *sep = ":";
uint8_t string_index = 0;
lua_pushnil(L);
while (lua_next(L, -3)) {
str[string_index] = lua_tostring(L, -1);
lua_pop(L, 1);
string_index++;
}
lua_pop(L, 1);
for (uint8_t i = 0; i < string_index; i++) {
for (uint8_t j = 0; j < 8; j++) {
ds18b20_device_rom[j] = strtoul(str[i], NULL, 16);
str[i] = strchr(str[i], *sep);
if (str[i] == NULL || *str[i] == '\0') break;
str[i]++;
}
ds18b20_set_device(ds18b20_device_rom);
}
// set conversion delay once to max if sensors with higher resolution still on the bus
ds18b20_device_res = 12;
return 0;
}
// Reads sensor values from all devices
// Lua: ds18b20.read(function(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) print(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) end, ROM[, FAMILY])
static int ds18b20_lua_read(lua_State *L) {
luaL_argcheck(L, (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION), 1, "Must be function");
lua_pushvalue(L, 1);
ds18b20_timer_ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (!lua_istable(L, 2)) {
return luaL_error(L, "wrong arg range");
}
if (lua_isnumber(L, 3)) {
ds18b20_device_family = luaL_checkinteger(L, 3);
onewire_target_search(ds18b20_bus_pin, ds18b20_device_family);
ds18b20_table_offset = -3;
} else {
ds18b20_table_offset = -2;
}
lua_pushvalue(L, 2);
ds18b20_table_ref = luaL_ref(L, LUA_REGISTRYINDEX);
lua_pushnil(L);
if (lua_next(L, ds18b20_table_offset)) {
lua_pop(L, 2);
ds18b20_device_search = 0;
} else {
ds18b20_device_search = 1;
}
os_timer_disarm(&ds18b20_timer);
// perform a temperature conversion for all sensors and set timer
onewire_reset(ds18b20_bus_pin);
onewire_write(ds18b20_bus_pin, DS18B20_ROM_SKIP, 0);
onewire_write(ds18b20_bus_pin, DS18B20_FUNC_CONVERT, 1);
os_timer_setfn(&ds18b20_timer, (os_timer_func_t *)ds18b20_lua_readoutdone, NULL);
switch (ds18b20_device_res) {
case (9):
os_timer_arm(&ds18b20_timer, 95, 0);
break;
case (10):
os_timer_arm(&ds18b20_timer, 190, 0);
break;
case (11):
os_timer_arm(&ds18b20_timer, 380, 0);
break;
case (12):
os_timer_arm(&ds18b20_timer, 760, 0);
break;
}
}
static int ds18b20_read_device(uint8_t *ds18b20_device_rom) {
lua_State *L = lua_getstate();
if (onewire_crc8(ds18b20_device_rom,7) == ds18b20_device_rom[7]) {
onewire_reset(ds18b20_bus_pin);
onewire_select(ds18b20_bus_pin, ds18b20_device_rom);
onewire_write(ds18b20_bus_pin, DS18B20_FUNC_POWER_READ, 0);
if (onewire_read(ds18b20_bus_pin)) ds18b20_device_par = 0;
else ds18b20_device_par = 1;
onewire_reset(ds18b20_bus_pin);
onewire_select(ds18b20_bus_pin, ds18b20_device_rom);
onewire_write(ds18b20_bus_pin, DS18B20_FUNC_SCRATCH_READ, 0);
onewire_read_bytes(ds18b20_bus_pin, ds18b20_device_scratchpad, 9);
if (onewire_crc8(ds18b20_device_scratchpad,8) == ds18b20_device_scratchpad[8]) {
lua_rawgeti(L, LUA_REGISTRYINDEX, ds18b20_timer_ref);
lua_pushinteger(L, ds18b20_device_index);
lua_pushfstring(L, "%d:%d:%d:%d:%d:%d:%d:%d", ds18b20_device_rom[0], ds18b20_device_rom[1], ds18b20_device_rom[2], ds18b20_device_rom[3], ds18b20_device_rom[4], ds18b20_device_rom[5], ds18b20_device_rom[6], ds18b20_device_rom[7]);
ds18b20_device_scratchpad_conf = (ds18b20_device_scratchpad[4] >> 5) + 9;
ds18b20_device_scratchpad_temp = ((int8_t)(ds18b20_device_scratchpad[1] << 4) + (ds18b20_device_scratchpad[0] >> 4) + ((double)(ds18b20_device_scratchpad[0] & 0x0F) / 16));
ds18b20_device_scratchpad_temp_dec = ((double)(ds18b20_device_scratchpad[0] & 0x0F) / 16 * 1000);
if (ds18b20_device_scratchpad_conf >= ds18b20_device_res) {
ds18b20_device_res = ds18b20_device_scratchpad_conf;
}
lua_pushinteger(L, ds18b20_device_scratchpad_conf);
lua_pushnumber(L, ds18b20_device_scratchpad_temp);
lua_pushinteger(L, ds18b20_device_scratchpad_temp_dec);
lua_pushinteger(L, ds18b20_device_par);
lua_pcall(L, 6, 0, 0);
ds18b20_device_index++;
}
}
}
static int ds18b20_lua_readoutdone(void) {
lua_State *L = lua_getstate();
os_timer_disarm(&ds18b20_timer);
ds18b20_device_index = 1;
// set conversion delay to min and change it after finding the sensor with the highest resolution setting
ds18b20_device_res = 9;
if (ds18b20_device_search) {
// iterate through all sensors on the bus and read temperature, resolution and parasitc settings
while (onewire_search(ds18b20_bus_pin, ds18b20_device_rom)) {
ds18b20_read_device(ds18b20_device_rom);
}
} else {
lua_rawgeti(L, LUA_REGISTRYINDEX, ds18b20_table_ref);
uint8_t table_len = lua_objlen(L, -1);
const char *str[table_len];
const char *sep = ":";
uint8_t string_index = 0;
lua_pushnil(L);
while (lua_next(L, -2)) {
str[string_index] = lua_tostring(L, -1);
lua_pop(L, 1);
string_index++;
}
lua_pop(L, 1);
for (uint8_t i = 0; i < string_index; i++) {
for (uint8_t j = 0; j < 8; j++) {
ds18b20_device_rom[j] = strtoul(str[i], NULL, 16);
str[i] = strchr(str[i], *sep);
if (str[i] == NULL || *str[i] == '\0') break;
str[i]++;
}
ds18b20_read_device(ds18b20_device_rom);
}
}
luaL_unref(L, LUA_REGISTRYINDEX, ds18b20_table_ref);
ds18b20_table_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, ds18b20_timer_ref);
ds18b20_timer_ref = LUA_NOREF;
}
static const LUA_REG_TYPE ds18b20_map[] = {
{ LSTRKEY( "read" ), LFUNCVAL(ds18b20_lua_read) },
{ LSTRKEY( "setting" ), LFUNCVAL(ds18b20_lua_setting) },
{ LSTRKEY( "setup" ), LFUNCVAL(ds18b20_lua_setup) },
{ LNILKEY, LNILVAL }
};
NODEMCU_MODULE(DS18B20, "ds18b20", ds18b20_map, NULL);

View File

@ -5,7 +5,10 @@
This module provides a simple interface to [BME280/BMP280 temperature/air presssure/humidity sensors](http://www.bosch-sensortec.com/bst/products/all_products/bme280) (Bosch Sensortec). This module provides a simple interface to [BME280/BMP280 temperature/air presssure/humidity sensors](http://www.bosch-sensortec.com/bst/products/all_products/bme280) (Bosch Sensortec).
Note that you must call [`setup()`](#bme280setup) before you can start reading values! !!! caution
Note that you must call [`setup()`](#bme280setup) before you can start reading values! Furthermore, there has to be a variable delay between some tens to hundreds of milliseconds between `setup()` and reading measurements. Instead of using a fixed delay you might also poll the sensor until data is delivered e.g. `humi()` not returning `nil` anymore.
## bme280.altitude() ## bme280.altitude()
@ -215,6 +218,7 @@ print(string.format("altitude=%s%d.%02d", curAltsgn<0 and "-" or "", curAlt/100,
``` ```
Or simpler and more efficient Or simpler and more efficient
```lua ```lua
alt=320 -- altitude of the measurement place alt=320 -- altitude of the measurement place

130
docs/en/modules/ds18b20.md Normal file
View File

@ -0,0 +1,130 @@
# DS18B20 Module
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2017-06-11 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [ds18b20.c](../../../app/modules/ds18b20.c)|
This module provides access to the DS18B20 1-Wire digital thermometer. Note that NodeMCU offers both a C module (this one) and [a Lua module for this sensor](https://github.com/nodemcu/nodemcu-firmware/tree/dev/lua_modules/ds18b20). See [#2003](https://github.com/nodemcu/nodemcu-firmware/pull/2003) for a discussion on the respective merits of them.
## ds18b20.read()
Issues a temperature conversion of all connected sensors on the onewire bus and returns the measurment results after a conversion delay in a callback function.
The returned measurements can be filtered through the ROM addresses passed as a table or by the family type.
The callback function gets invoked for every specified sensor.
#### Syntax
`ds18b20.read(CALLBACK, ROM[, FAMILY_ADDRESS])`
#### Parameters
- `CALLBACK` callback function executed for each sensor
* e.g. `function(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) print(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) end`
- `ROM` table which contains the addresses for the specified sensors, or left empty to perform a onewire bus search for all sensors
* e.g. `{"28:FF:FF:FF:FF:FF:FF:FF","28:FF:FF:FF:FF:FF:FF:FF"}`, `{}`
- `FAMILY_ADDRESS` optional to limit the search for devices to a specific family type
* e.g `0x28`
#### Returns
`nil`
#### Callback function parameters
- `INDEX` index of the sensor on the bus
- `ROM` sensors 64-bit lasered rom code
* `28:FF:FF:FF:FF:FF:FF:FF` LSB, 8-bit family code, 48-bit serial number, MSB 8-bit crc
- `RES` temperature resolution
- `TEMP` temperature
- `TEMP_DEC` temperature decimals for integer firmware
- `PAR` sensor parasitic flag
!!! note
If using float firmware then `temp` is a floating point number. On an integer firmware, the final value has to be concatenated from `temp` and `temp_dec`.
#### Example
```lua
local ow_pin = 3
ds18b20.setup(ow_pin)
-- read all sensors and print all measurement results
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end,{});
-- read only sensors with family type 0x28 and print all measurement results
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end,{},0x28);
-- save device roms in a variable
local addr = {}
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
addr[ind] = {string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)"))}
end,{});
-- read only sensors listed in the variable addr
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end,addr);
-- print only parasitic sensors
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
if (par == 1) then
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end
end,{});
-- print if temperature is greater or less than a defined value
ds18b20.read(
function(ind,rom,res,temp,tdec,par)
if (t > 25) then
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end
if (t < 20) then
print(ind,string.format("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",string.match(rom,"(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+):(%d+)")),res,temp,tdec,par)
end
end,{});
```
## ds18b20.setting()
Configuration of the temperature resolution settings.
#### Syntax
`ds18b20.setting(ROM, RES)`
#### Parameters
- `ROM` table which contains the addresses for the specified sensors, or empty for all sensors
* e.g. `{"28:FF:FF:FF:FF:FF:FF:FF","28:FF:FF:FF:FF:FF:FF:FF"}`, `{}`
- `RES` temperature bit resolution
* `9` - `12`
#### Returns
`nil`
#### Example
```lua
local ow_pin = 3
ds18b20.setup(ow_pin)
ds18b20.setting({"28:FF:FF:FF:FF:FF:FF:FF","28:FF:FF:FF:FF:FF:FF:FF"}, 9)
```
## ds18b20.setup()
Initializes the onewire bus on the selected pin.
#### Syntax
`ds18b20.setup(OW_BUS_PIN)`
#### Parameters
- `OW_BUS_PIN`
* `1` - `12`
#### Returns
`nil`
#### Example
```lua
local ow_pin = 3
ds18b20.setup(ow_pin)
```

View File

@ -221,6 +221,10 @@ m:subscribe("/topic",0, function(conn) print("subscribe success") end)
m:subscribe({["topic/0"]=0,["topic/1"]=1,topic2=2}, function(conn) print("subscribe success") end) m:subscribe({["topic/0"]=0,["topic/1"]=1,topic2=2}, function(conn) print("subscribe success") end)
``` ```
!!! caution
Rather than calling `subscribe` multiple times you should use the multiple topics syntax shown in the above example if you want to subscribe to more than one topic at once.
## mqtt.client:unsubscribe() ## mqtt.client:unsubscribe()
Unsubscribes from one or several topics. Unsubscribes from one or several topics.

View File

@ -1,4 +1,7 @@
# DS18B20 Module # DS18B20 Module
This is a Lua module for the DS18B20 1-Wire digital thermometer. Note that NodeMCU offers both a Lua module (this one) and [a C module for this sensor](http://nodemcu.readthedocs.io/en/latest/en/modules/ds18b20/). See [#2003](https://github.com/nodemcu/nodemcu-firmware/pull/2003) for a discussion on the respective merits of them.
## Require ## Require
```lua ```lua
ds18b20 = require("ds18b20") ds18b20 = require("ds18b20")
@ -8,33 +11,41 @@ ds18b20 = require("ds18b20")
ds18b20 = nil ds18b20 = nil
package.loaded["ds18b20"]=nil package.loaded["ds18b20"]=nil
``` ```
<a id="ds18b20_setup"></a>
## readTemp() # Methods
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.
## read_temp()
Scans the bus for DS18B20 sensors (optional), 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 module requires `ow` module.
The also module uses `encoder` module for printing debug information with more readable representation of sensor address (`encoder.toHex()`).
#### Syntax #### Syntax
`readTemp(callback, pin)` `read_temp(callback, pin, unit, force_search, save_search)`
#### Parameters #### 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). - `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. - `pin` pin of the one-wire bus. If nil, GPIO0 (3) is used.
- `unit` unit can be Celsius ("C" or ds18b20.C), Kelvin ("K" or ds18b20.K) or Fahrenheit ("F" or ds18b20.F). If not specified (nil) latest used unit is used.
- `force_search` if not nil a bus search for devices is performed before readout. If nil the existing list of sensors in memory is used. If the bus has not been searched yet the search performed as well.
- `save_search` if not nil found sensors are saved to the file `ds18b20_save.lc`. When `read_temp` is called, list of sensors in memory is empty and file `ds18b20_save.lc` is present then sensor addresses are loaded from file - usefull when running from batteries & deepsleep - immediate readout is performed (no bus scan).
#### Returns #### Returns
nil nil
#### Example #### Example
```lua ```lua
t = require("ds18b20") local t = require("ds18b20")
pin = 3 -- gpio0 = 3, gpio2 = 4 local pin = 3 -- gpio0 = 3, gpio2 = 4
function readout(temp) local function readout(temp)
if t.sens then
print("Total number of DS18B20 sensors: ".. #t.sens)
for i, s in ipairs(t.sens) do
print(string.format(" sensor #%d address: %s%s", i, ('%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X'):format(s:byte(1,8)), s:byte(9) == 1 and " (parasite)" or ""))
end
end
for addr, temp in pairs(temp) do for addr, temp in pairs(temp) do
print(string.format("Sensor %s: %s 'C", encoder.toHex(addr), temp)) print(string.format("Sensor %s: %s °C", ('%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X'):format(addr:byte(1,8)), temp))
end end
-- Module can be released when it is no longer needed -- Module can be released when it is no longer needed
@ -42,13 +53,15 @@ function readout(temp)
package.loaded["ds18b20"]=nil package.loaded["ds18b20"]=nil
end end
-- t:readTemp(readout) -- default pin value is 3 t:read_temp(readout, pin, t.C)```
t:readTemp(readout, pin)
if t.sens then ## enable_debug()
print("Total number of DS18B20 sensors: "..table.getn(t.sens)) Enables debug output of the module.
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 "")) # Properties
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 ## sens
end A table with sensors present on the bus. It includes its address (8 bytes) and information whether the sensor is parasite-powered (9-th byte, 0 or 1).
```
## temp
A table with readout values (also passed as a parameter to callback function). It is addressed by sensor addresses.

View File

@ -1,26 +1,52 @@
-- 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 pin = 3 -- gpio0 = 3, gpio2 = 4
function readout(temp) local function readout(temp)
if t.sens then
print("Total number of DS18B20 sensors: ".. #t.sens)
for i, s in ipairs(t.sens) do
print(string.format(" sensor #%d address: %s%s", i, ('%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X'):format(s:byte(1,8)), s:byte(9) == 1 and " (parasite)" or ""))
end
end
for addr, temp in pairs(temp) do for addr, temp in pairs(temp) do
-- print(string.format("Sensor %s: %s 'C", addr, temp)) print(string.format("Sensor %s: %s °C", ('%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X'):format(addr:byte(1,8)), 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 end
-- Module can be released when it is no longer needed -- Module can be released when it is no longer needed
t = nil --t = nil
package.loaded["ds18b20"]=nil --package.loaded["ds18b20"]=nil
end end
-- t:readTemp(readout) -- default pin value is 3 t:enable_debug()
t:readTemp(readout, pin) file.remove("ds18b20_save.lc") -- remove saved addresses
if t.sens then print("=============================================", node.heap())
print("Total number of DS18B20 sensors: "..table.getn(t.sens)) print("first call, no addresses in flash, search is performed")
for i, s in ipairs(t.sens) do t:read_temp(readout, pin, t.C)
-- 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 base64 encoding is preferred when encoder module is available tmr.create():alarm(2000, tmr.ALARM_SINGLE, function()
end print("=============================================", node.heap())
end print("second readout, no new search, found addresses are used")
t:read_temp(readout, pin)
tmr.create():alarm(2000, tmr.ALARM_SINGLE, function()
print("=============================================", node.heap())
print("force search again")
t:read_temp(readout, pin, nil, true)
tmr.create():alarm(2000, tmr.ALARM_SINGLE, function()
print("=============================================", node.heap())
print("save search results")
t:read_temp(readout, pin, nil, false, true)
tmr.create():alarm(2000, tmr.ALARM_SINGLE, function()
print("=============================================", node.heap())
print("use saved addresses")
t.sens={}
t:read_temp(readout, pin)
end)
end)
end)
end)

View File

@ -11,8 +11,7 @@ function readout(temp)
"<b>ESP8266</b></br>" "<b>ESP8266</b></br>"
for addr, temp in pairs(temp) do 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>", ('%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X '):format(addr:byte(1,8)), 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 end
resp = resp .. resp = resp ..
@ -30,7 +29,7 @@ srv=net.createServer(net.TCP)
srv:listen(port, srv:listen(port,
function(conn) function(conn)
gconn = conn gconn = conn
-- t:readTemp(readout) -- default pin value is 3 -- t:read_temp(readout) -- default pin value is 3
t:readTemp(readout, pin) t:read_temp(readout, pin)
end end
) )

View File

@ -1,121 +1,223 @@
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- DS18B20 one wire module for NODEMCU -- DS18B20 one wire module for NODEMCU
-- by @voborsky, @devsaurus -- NODEMCU TEAM
-- encoder module is needed only for debug output; lines can be removed if no -- LICENCE: http://opensource.org/licenses/MIT
-- debug output is needed and/or encoder module is missing -- @voborsky, @devsaurus, TerryE 26 Mar 2017
-- ----------------------------------------------------------------------------------------------------------------------------------------------------------------
-- by default the module is for integer version, comment integer version and local modname = ...
-- uncomment float version part for float version
-- Used modules and functions
local table, string, ow, tmr, print, type, tostring, pcall, ipairs =
table, string, ow, tmr, print, 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
table, string, tmr, ow = nil, nil, nil, nil
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
return({ local function to_string(addr, esc)
pin=3, if type(addr) == 'string' and #addr == 8 then
sens={}, return ( esc == true and
temp={}, '"\\%u\\%u\\%u\\%u\\%u\\%u\\%u\\%u"' or
'%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X '):format(addr:byte(1,8))
conversion = function(self) else
local pin = self.pin return tostring(addr)
for i,s in ipairs(self.sens) do
if s.status == 0 then
print("starting conversion:", encoder.toHex(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
end end
tmr.create():alarm(750, tmr.ALARM_SINGLE, function() self:readout() end)
end,
readTemp = function(self, cb, lpin) local function readout(self)
if lpin then self.pin = lpin end
local pin = self.pin
self.cb = cb
self.temp={}
ow.setup(pin)
self.sens={}
ow.reset_search(pin)
-- 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.toHex(addr), parasite == 1 and "parasite" or " ")
end
addr = ow.search(pin)
tmr.wdclr()
end
-- place powered sensors first
table.sort(self.sens, function(a,b) return a.parasite<b.parasite end)
node.task.post(node.task.MEDIUM_PRIORITY, function() self:conversion() end)
end,
readout=function(self)
local pin = self.pin
local next = false local next = false
if not self.sens then return 0 end local sens = self.sens
for i,s in ipairs(self.sens) do local temp = self.temp
-- print(encoder.toHex(s.addr), s.status) for i, s in ipairs(sens) do
if s.status == 1 then if status[i] == 1 then
ow.reset(pin) ow_reset(pin)
ow.select(pin, s.addr) -- select the sensor local addr = s:sub(1,8)
ow.write(pin, 0xBE, 0) -- READ_SCRATCHPAD ow_select(pin, addr) -- select the sensor
data = ow.read_bytes(pin, 9) ow_write(pin, READ_SCRATCHPAD, MODE)
data = ow_read_bytes(pin, 9)
local t=(data:byte(1)+data:byte(2)*256) local t=(data:byte(1)+data:byte(2)*256)
if (t > 0x7fff) then t = t - 0x10000 end -- t is actually signed so process the sign bit and adjust for fractional bits
if (s.addr:byte(1) == 0x28) then -- the DS18B20 family has 4 fractional bits and the DS18S20s, 1 fractional bit
t = t * 625 -- DS18B20, 4 fractional bits t = ((t <= 32767) and t or t - 65536) *
else ((addr:byte(1) == DS18B20FAMILY) and 625 or 5000)
t = t * 5000 -- DS18S20, 1 fractional bit
end
if 1/2 == 0 then if 1/2 == 0 then
-- integer version -- 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 sgn = t<0 and -1 or 1
local tA = sgn*t local tA = sgn*t
local tH=tA/10000 local tH=tA/10000
local tL=(tA%10000)/1000 + ((tA%1000)/100 >= 5 and 1 or 0) local tL=(tA%10000)/1000 + ((tA%1000)/100 >= 5 and 1 or 0)
if tH and (tH~=85) then if tH and (t~=850000) then
self.temp[s.addr]=(sgn<0 and "-" or "")..tH.."."..tL temp[addr]=(sgn<0 and "-" or "")..tH.."."..tL
print(encoder.toHex(s.addr),(sgn<0 and "-" or "")..tH.."."..tL) debugPrint(to_string(addr),(sgn<0 and "-" or "")..tH.."."..tL)
s.status = 2 status[i] = 2
end end
-- end integer version -- end integer version
else else
-- float version -- float version
if t and (math.floor(t/10000)~=85) then if t and (math_floor(t/10000)~=85) then
self.temp[s.addr]=t/10000 t = t / 10000
print(encoder.toHex(s.addr), t) if unit == 'F' then
s.status = 2 t = t * 18/10 + 32
elseif unit == 'K' then
t = t + 27315/100
end
self.temp[addr]=t
debugPrint(to_string(addr), t)
status[i] = 2
end end
-- end float version -- end float version
end end
end end
next = next or s.status == 0 next = next or status[i] == 0
end end
if next then if next then
node.task.post(node.task.MEDIUM_PRIORITY, function() self:conversion() end) node_task_post(node_task_LOW_PRIORITY, function() return conversion(self) end)
else else
self.sens = nil --sens = {}
if self.cb then if cb then
node.task.post(node.task.MEDIUM_PRIORITY, function() self.cb(self.temp) end) node_task_post(node_task_LOW_PRIORITY, function() return cb(temp) end)
end end
end end
end
local function conversion(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) -- select the sensor
ow_write(pin, CONVERT_T, MODE) -- and start conversion
for i, s in ipairs(sens) do status[i] = 1 end
else
for i, s in ipairs(sens) do
if status[i] == 0 then
local addr, parasite = s:sub(1,8), s:byte(9)
debugPrint("starting conversion:", to_string(addr), parasite == 1 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 == 1 then break end -- parasite sensor blocks bus during conversion
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 temp = self.temp
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, s in ipairs(sens) do status[i] = 0 end
end end
}) local function cycle()
debugPrint("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) -- {addr=addr, parasite=parasite, status=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

View File

@ -48,6 +48,7 @@ pages:
- 'cron': 'en/modules/cron.md' - 'cron': 'en/modules/cron.md'
- 'crypto': 'en/modules/crypto.md' - 'crypto': 'en/modules/crypto.md'
- 'dht': 'en/modules/dht.md' - 'dht': 'en/modules/dht.md'
- 'ds18b20': 'en/modules/ds18b20.md'
- 'encoder': 'en/modules/encoder.md' - 'encoder': 'en/modules/encoder.md'
- 'enduser setup': 'en/modules/enduser-setup.md' - 'enduser setup': 'en/modules/enduser-setup.md'
- 'file': 'en/modules/file.md' - 'file': 'en/modules/file.md'