2017-08-16 21:04:23 +02:00
//***************************************************************************
// 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 ;
}
2018-04-13 21:41:14 +02:00
# include "pm/swtimer.h"
2017-08-16 21:04:23 +02:00
// 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 ) ;
2018-04-13 21:41:14 +02:00
SWTIMER_REG_CB ( ds18b20_lua_readoutdone , SWTIMER_DROP ) ;
//The function ds18b20_lua_readoutdone reads the temperature from the sensor(s) after a set amount of time depending on temperature resolution
//MY guess: If this timer manages to get suspended before it fires and the temperature data is time sensitive then resulting data would be invalid and should be discarded
2017-08-16 21:04:23 +02:00
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 ) ;