|
| 1 | +//*************************************************************************** |
| 2 | +// DS18B20 module for ESP8266 with nodeMCU |
| 3 | +// fetchbot @github |
| 4 | +// MIT license, http://opensource.org/licenses/MIT |
| 5 | +//*************************************************************************** |
| 6 | + |
| 7 | +#include "module.h" |
| 8 | +#include "lauxlib.h" |
| 9 | +#include "platform.h" |
| 10 | +#include "osapi.h" |
| 11 | +#include "driver/onewire.h" |
| 12 | +#include "c_stdio.h" |
| 13 | +#include "c_stdlib.h" |
| 14 | + |
| 15 | +//*************************************************************************** |
| 16 | +// OW ROM COMMANDS |
| 17 | +//*************************************************************************** |
| 18 | + |
| 19 | +#define DS18B20_ROM_SEARCH (0xF0) |
| 20 | +#define DS18B20_ROM_READ (0x33) |
| 21 | +#define DS18B20_ROM_MATCH (0x55) |
| 22 | +#define DS18B20_ROM_SKIP (0xCC) |
| 23 | +#define DS18B20_ROM_SEARCH_ALARM (0xEC) |
| 24 | + |
| 25 | +//*************************************************************************** |
| 26 | +// OW FUNCTION COMMANDS |
| 27 | +//*************************************************************************** |
| 28 | + |
| 29 | +#define DS18B20_FUNC_CONVERT (0x44) |
| 30 | +#define DS18B20_FUNC_SCRATCH_WRITE (0x4E) |
| 31 | +#define DS18B20_FUNC_SCRATCH_READ (0xBE) |
| 32 | +#define DS18B20_FUNC_SCRATCH_COPY (0x48) |
| 33 | +#define DS18B20_FUNC_E2_RECALL (0xB8) |
| 34 | +#define DS18B20_FUNC_POWER_READ (0xB4) |
| 35 | + |
| 36 | +//*************************************************************************** |
| 37 | +// Initial EEPROM values |
| 38 | +//*************************************************************************** |
| 39 | + |
| 40 | +#define DS18B20_EEPROM_TH (0x4B) // 75 degree |
| 41 | +#define DS18B20_EEPROM_TL (0x46) // 70 degree |
| 42 | +#define DS18B20_EEPROM_RES (0x7F) // 12 bit resolution |
| 43 | + |
| 44 | +//*************************************************************************** |
| 45 | + |
| 46 | +static uint8_t ds18b20_bus_pin; |
| 47 | +static uint8_t ds18b20_device_family; |
| 48 | +static uint8_t ds18b20_device_search = 0; |
| 49 | +static uint8_t ds18b20_device_index; |
| 50 | +static uint8_t ds18b20_device_par; |
| 51 | +static uint8_t ds18b20_device_conf[3]; |
| 52 | +static uint8_t ds18b20_device_rom[8]; |
| 53 | +static uint8_t ds18b20_device_scratchpad[9]; |
| 54 | +static double ds18b20_device_scratchpad_temp; |
| 55 | +static int ds18b20_device_scratchpad_temp_dec; |
| 56 | +static uint8_t ds18b20_device_scratchpad_conf; |
| 57 | +static uint8_t ds18b20_device_res = 12; // 12 bit resolution (750ms conversion time) |
| 58 | + |
| 59 | +os_timer_t ds18b20_timer; // timer for conversion delay |
| 60 | +int ds18b20_timer_ref; // callback when readout is ready |
| 61 | + |
| 62 | +int ds18b20_table_ref; |
| 63 | +static int ds18b20_table_offset; |
| 64 | + |
| 65 | +static int ds18b20_lua_readoutdone(void); |
| 66 | + |
| 67 | +// Setup onewire bus for DS18B20 temperature sensors |
| 68 | +// Lua: ds18b20.setup(OW_BUS_PIN) |
| 69 | +static int ds18b20_lua_setup(lua_State *L) { |
| 70 | + // check ow bus pin value |
| 71 | + if (!lua_isnumber(L, 1) || lua_isnumber(L, 1) == 0) { |
| 72 | + return luaL_error(L, "wrong 1-wire pin"); |
| 73 | + } |
| 74 | + |
| 75 | + ds18b20_bus_pin = luaL_checkinteger(L, 1); |
| 76 | + MOD_CHECK_ID(ow, ds18b20_bus_pin); |
| 77 | + onewire_init(ds18b20_bus_pin); |
| 78 | +} |
| 79 | + |
| 80 | +static int ds18b20_set_device(uint8_t *ds18b20_device_rom) { |
| 81 | + onewire_reset(ds18b20_bus_pin); |
| 82 | + onewire_select(ds18b20_bus_pin, ds18b20_device_rom); |
| 83 | + onewire_write(ds18b20_bus_pin, DS18B20_FUNC_SCRATCH_WRITE, 0); |
| 84 | + onewire_write_bytes(ds18b20_bus_pin, ds18b20_device_conf, 3, 0); |
| 85 | +} |
| 86 | + |
| 87 | +// Change sensor settings |
| 88 | +// Lua: ds18b20.setting(ROM, RES) |
| 89 | +static int ds18b20_lua_setting(lua_State *L) { |
| 90 | + // check rom table and resolution setting |
| 91 | + if (!lua_istable(L, 1) || !lua_isnumber(L, 2)) { |
| 92 | + return luaL_error(L, "wrong arg range"); |
| 93 | + } |
| 94 | + |
| 95 | + ds18b20_device_res = luaL_checkinteger(L, 2); |
| 96 | + |
| 97 | + if (!((ds18b20_device_res == 9) || (ds18b20_device_res == 10) || (ds18b20_device_res == 11) || (ds18b20_device_res == 12))) { |
| 98 | + return luaL_error(L, "Invalid argument: resolution"); |
| 99 | + } |
| 100 | + |
| 101 | + // no change to th and tl setting |
| 102 | + ds18b20_device_conf[0] = DS18B20_EEPROM_TH; |
| 103 | + ds18b20_device_conf[1] = DS18B20_EEPROM_TL; |
| 104 | + ds18b20_device_conf[2] = ((ds18b20_device_res - 9) << 5) + 0x1F; |
| 105 | + |
| 106 | + uint8_t table_len = lua_objlen(L, 1); |
| 107 | + |
| 108 | + const char *str[table_len]; |
| 109 | + const char *sep = ":"; |
| 110 | + |
| 111 | + uint8_t string_index = 0; |
| 112 | + |
| 113 | + lua_pushnil(L); |
| 114 | + while (lua_next(L, -3)) { |
| 115 | + str[string_index] = lua_tostring(L, -1); |
| 116 | + lua_pop(L, 1); |
| 117 | + string_index++; |
| 118 | + } |
| 119 | + lua_pop(L, 1); |
| 120 | + |
| 121 | + for (uint8_t i = 0; i < string_index; i++) { |
| 122 | + for (uint8_t j = 0; j < 8; j++) { |
| 123 | + ds18b20_device_rom[j] = strtoul(str[i], NULL, 16); |
| 124 | + str[i] = strchr(str[i], *sep); |
| 125 | + if (str[i] == NULL || *str[i] == '\0') break; |
| 126 | + str[i]++; |
| 127 | + } |
| 128 | + ds18b20_set_device(ds18b20_device_rom); |
| 129 | + } |
| 130 | + |
| 131 | + // set conversion delay once to max if sensors with higher resolution still on the bus |
| 132 | + ds18b20_device_res = 12; |
| 133 | + |
| 134 | + return 0; |
| 135 | +} |
| 136 | + |
| 137 | +// Reads sensor values from all devices |
| 138 | +// Lua: ds18b20.read(function(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) print(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) end, ROM[, FAMILY]) |
| 139 | +static int ds18b20_lua_read(lua_State *L) { |
| 140 | + |
| 141 | + luaL_argcheck(L, (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION), 1, "Must be function"); |
| 142 | + |
| 143 | + lua_pushvalue(L, 1); |
| 144 | + ds18b20_timer_ref = luaL_ref(L, LUA_REGISTRYINDEX); |
| 145 | + |
| 146 | + if (!lua_istable(L, 2)) { |
| 147 | + return luaL_error(L, "wrong arg range"); |
| 148 | + } |
| 149 | + |
| 150 | + if (lua_isnumber(L, 3)) { |
| 151 | + ds18b20_device_family = luaL_checkinteger(L, 3); |
| 152 | + onewire_target_search(ds18b20_bus_pin, ds18b20_device_family); |
| 153 | + ds18b20_table_offset = -3; |
| 154 | + } else { |
| 155 | + ds18b20_table_offset = -2; |
| 156 | + } |
| 157 | + |
| 158 | + lua_pushvalue(L, 2); |
| 159 | + ds18b20_table_ref = luaL_ref(L, LUA_REGISTRYINDEX); |
| 160 | + |
| 161 | + lua_pushnil(L); |
| 162 | + if (lua_next(L, ds18b20_table_offset)) { |
| 163 | + lua_pop(L, 2); |
| 164 | + ds18b20_device_search = 0; |
| 165 | + } else { |
| 166 | + ds18b20_device_search = 1; |
| 167 | + } |
| 168 | + |
| 169 | + os_timer_disarm(&ds18b20_timer); |
| 170 | + |
| 171 | + // perform a temperature conversion for all sensors and set timer |
| 172 | + onewire_reset(ds18b20_bus_pin); |
| 173 | + onewire_write(ds18b20_bus_pin, DS18B20_ROM_SKIP, 0); |
| 174 | + onewire_write(ds18b20_bus_pin, DS18B20_FUNC_CONVERT, 1); |
| 175 | + os_timer_setfn(&ds18b20_timer, (os_timer_func_t *)ds18b20_lua_readoutdone, NULL); |
| 176 | + |
| 177 | + switch (ds18b20_device_res) { |
| 178 | + case (9): |
| 179 | + os_timer_arm(&ds18b20_timer, 95, 0); |
| 180 | + break; |
| 181 | + case (10): |
| 182 | + os_timer_arm(&ds18b20_timer, 190, 0); |
| 183 | + break; |
| 184 | + case (11): |
| 185 | + os_timer_arm(&ds18b20_timer, 380, 0); |
| 186 | + break; |
| 187 | + case (12): |
| 188 | + os_timer_arm(&ds18b20_timer, 760, 0); |
| 189 | + break; |
| 190 | + } |
| 191 | +} |
| 192 | + |
| 193 | +static int ds18b20_read_device(uint8_t *ds18b20_device_rom) { |
| 194 | + lua_State *L = lua_getstate(); |
| 195 | + |
| 196 | + if (onewire_crc8(ds18b20_device_rom,7) == ds18b20_device_rom[7]) { |
| 197 | + |
| 198 | + onewire_reset(ds18b20_bus_pin); |
| 199 | + onewire_select(ds18b20_bus_pin, ds18b20_device_rom); |
| 200 | + onewire_write(ds18b20_bus_pin, DS18B20_FUNC_POWER_READ, 0); |
| 201 | + |
| 202 | + if (onewire_read(ds18b20_bus_pin)) ds18b20_device_par = 0; |
| 203 | + else ds18b20_device_par = 1; |
| 204 | + |
| 205 | + onewire_reset(ds18b20_bus_pin); |
| 206 | + onewire_select(ds18b20_bus_pin, ds18b20_device_rom); |
| 207 | + onewire_write(ds18b20_bus_pin, DS18B20_FUNC_SCRATCH_READ, 0); |
| 208 | + onewire_read_bytes(ds18b20_bus_pin, ds18b20_device_scratchpad, 9); |
| 209 | + |
| 210 | + if (onewire_crc8(ds18b20_device_scratchpad,8) == ds18b20_device_scratchpad[8]) { |
| 211 | + |
| 212 | + lua_rawgeti(L, LUA_REGISTRYINDEX, ds18b20_timer_ref); |
| 213 | + |
| 214 | + lua_pushinteger(L, ds18b20_device_index); |
| 215 | + |
| 216 | + 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]); |
| 217 | + |
| 218 | + ds18b20_device_scratchpad_conf = (ds18b20_device_scratchpad[4] >> 5) + 9; |
| 219 | + ds18b20_device_scratchpad_temp = ((int8_t)(ds18b20_device_scratchpad[1] << 4) + (ds18b20_device_scratchpad[0] >> 4) + ((double)(ds18b20_device_scratchpad[0] & 0x0F) / 16)); |
| 220 | + ds18b20_device_scratchpad_temp_dec = ((double)(ds18b20_device_scratchpad[0] & 0x0F) / 16 * 1000); |
| 221 | + |
| 222 | + if (ds18b20_device_scratchpad_conf >= ds18b20_device_res) { |
| 223 | + ds18b20_device_res = ds18b20_device_scratchpad_conf; |
| 224 | + } |
| 225 | + |
| 226 | + lua_pushinteger(L, ds18b20_device_scratchpad_conf); |
| 227 | + lua_pushnumber(L, ds18b20_device_scratchpad_temp); |
| 228 | + lua_pushinteger(L, ds18b20_device_scratchpad_temp_dec); |
| 229 | + |
| 230 | + lua_pushinteger(L, ds18b20_device_par); |
| 231 | + |
| 232 | + lua_pcall(L, 6, 0, 0); |
| 233 | + |
| 234 | + ds18b20_device_index++; |
| 235 | + } |
| 236 | + } |
| 237 | +} |
| 238 | + |
| 239 | +static int ds18b20_lua_readoutdone(void) { |
| 240 | + |
| 241 | + lua_State *L = lua_getstate(); |
| 242 | + os_timer_disarm(&ds18b20_timer); |
| 243 | + |
| 244 | + ds18b20_device_index = 1; |
| 245 | + // set conversion delay to min and change it after finding the sensor with the highest resolution setting |
| 246 | + ds18b20_device_res = 9; |
| 247 | + |
| 248 | + if (ds18b20_device_search) { |
| 249 | + // iterate through all sensors on the bus and read temperature, resolution and parasitc settings |
| 250 | + while (onewire_search(ds18b20_bus_pin, ds18b20_device_rom)) { |
| 251 | + ds18b20_read_device(ds18b20_device_rom); |
| 252 | + } |
| 253 | + } else { |
| 254 | + lua_rawgeti(L, LUA_REGISTRYINDEX, ds18b20_table_ref); |
| 255 | + uint8_t table_len = lua_objlen(L, -1); |
| 256 | + |
| 257 | + const char *str[table_len]; |
| 258 | + const char *sep = ":"; |
| 259 | + |
| 260 | + uint8_t string_index = 0; |
| 261 | + |
| 262 | + lua_pushnil(L); |
| 263 | + while (lua_next(L, -2)) { |
| 264 | + str[string_index] = lua_tostring(L, -1); |
| 265 | + lua_pop(L, 1); |
| 266 | + string_index++; |
| 267 | + } |
| 268 | + lua_pop(L, 1); |
| 269 | + |
| 270 | + for (uint8_t i = 0; i < string_index; i++) { |
| 271 | + for (uint8_t j = 0; j < 8; j++) { |
| 272 | + ds18b20_device_rom[j] = strtoul(str[i], NULL, 16); |
| 273 | + str[i] = strchr(str[i], *sep); |
| 274 | + if (str[i] == NULL || *str[i] == '\0') break; |
| 275 | + str[i]++; |
| 276 | + } |
| 277 | + ds18b20_read_device(ds18b20_device_rom); |
| 278 | + } |
| 279 | + } |
| 280 | + |
| 281 | + luaL_unref(L, LUA_REGISTRYINDEX, ds18b20_table_ref); |
| 282 | + ds18b20_table_ref = LUA_NOREF; |
| 283 | + |
| 284 | + luaL_unref(L, LUA_REGISTRYINDEX, ds18b20_timer_ref); |
| 285 | + ds18b20_timer_ref = LUA_NOREF; |
| 286 | +} |
| 287 | + |
| 288 | +static const LUA_REG_TYPE ds18b20_map[] = { |
| 289 | + { LSTRKEY( "read" ), LFUNCVAL(ds18b20_lua_read) }, |
| 290 | + { LSTRKEY( "setting" ), LFUNCVAL(ds18b20_lua_setting) }, |
| 291 | + { LSTRKEY( "setup" ), LFUNCVAL(ds18b20_lua_setup) }, |
| 292 | + { LNILKEY, LNILVAL } |
| 293 | +}; |
| 294 | + |
| 295 | +NODEMCU_MODULE(DS18B20, "ds18b20", ds18b20_map, NULL); |
0 commit comments