Initial commit

This commit is contained in:
Rafal Zajac
2017-11-12 22:49:10 +01:00
commit 74d0876c59
39 changed files with 2503 additions and 0 deletions

View File

@ -0,0 +1,38 @@
# Copyright 2017 Rafal Zajac <rzajac@gmail.com>.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
project(esp_ds18b20 C)
find_package(esp_ow REQUIRED)
find_package(esp_eb REQUIRED)
find_package(esp_tim REQUIRED)
add_library(esp_ds18b20 STATIC
esp_ds18b20.c
include/esp_ds18b20.h)
target_include_directories(esp_ds18b20 PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
${esp_ow_INCLUDE_DIRS}
${esp_eb_INCLUDE_DIRS}
${esp_tim_INCLUDE_DIRS}
${ESP_USER_CONFIG_DIR})
esp_gen_lib(esp_ds18b20
${ESP_CMAKE_FIND_DIR}
${esp_ow_LIBRARIES}
${esp_eb_LIBRARIES}
${esp_tim_LIBRARIES})

View File

@ -0,0 +1,52 @@
# Copyright 2017 Rafal Zajac <rzajac@gmail.com>.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Try to find esp_ds18b20
#
# Once done this will define:
#
# esp_ds18b20_FOUND - System found the library.
# esp_ds18b20_INCLUDE_DIR - The library include directory.
# esp_ds18b20_INCLUDE_DIRS - If library has dependencies this will be set
# to <lib_name>_INCLUDE_DIR [<dep1_name_INCLUDE_DIRS>, ...].
# esp_ds18b20_LIBRARY - The path to the library.
# esp_ds18b20_LIBRARIES - The dependencies to link to use the library.
# It will have a form of <lib_name>_LIBRARY [dep1_name_LIBRARIES, ...].
#
find_path(esp_ds18b20_INCLUDE_DIR esp_ds18b20.h)
find_library(esp_ds18b20_LIBRARY NAMES esp_ds18b20)
find_package(esp_ow REQUIRED)
find_package(esp_eb REQUIRED)
find_package(esp_tim REQUIRED)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(esp_ds18b20
DEFAULT_MSG
esp_ds18b20_LIBRARY
esp_ds18b20_INCLUDE_DIR)
set(esp_ds18b20_INCLUDE_DIRS
${esp_ds18b20_INCLUDE_DIR}
${esp_ow_INCLUDE_DIRS}
${esp_eb_INCLUDE_DIRS}
${esp_tim_INCLUDE_DIRS})
set(esp_ds18b20_LIBRARIES
${esp_ds18b20_LIBRARY}
${esp_ow_LIBRARIES}
${esp_eb_LIBRARIES}
${esp_tim_LIBRARIES})

45
src/esp_ds18b20/README.md Normal file
View File

@ -0,0 +1,45 @@
## DS18B20 driver for ESP8266.
Full featured DS18B20 digital thermometer driver.
![DS18B20](../../doc/ds18b20.jpg)
Before you can get the temperature from DS18B20 you have to call
`esp_ds18b20_init`. You need to call it only once unless you change the GPIO
pin setup somewhere else in your code.
To find device(s) on the OneWire bus you can use `esp_ds18b20_search` which
will give you linked list of `esp_ow_device` devices.
It's up to the user to release memory allocated for the linked list with
library provided helper function `esp_ds18b20_free_list` when it's not needed
anymore.
Most of the operations with the DS18B20 library is based on passing pointers
to the `esp_ow_device` structure which has pointer to `esp_ds18b20_st`
status structure. The `esp_ds18b20_st` keeps track of last scratch pad read
the last temperature conversion.
```
esp_ow_device *device;
esp_ds18b20_st *st = device->custom;
```
If you already know the ROM address of your device you can use
`esp_ds18b20_new_dev`:
```
uint8_t rom[8] = {0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0}
esp_ow_dev *device = esp_ds18b20_new_dev(rom);
```
With DS18B20 temperature measurement takes between 94 and 750ms depending
on resolution. You don't want to block the CPU for that long waiting for
example using some kind of delay. That's why library is using event bus
(esp_eb) to emmit events when the temperature conversion is ready to read.
Check [example program](../../examples/ds18b20_temp) to see how it should be
done.
See driver documentation in [esp_ds18b20.h](include/esp_ds18b20.h) header file
for more details.

View File

@ -0,0 +1,305 @@
/*
* Copyright 2017 Rafal Zajac <rzajac@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <esp_ds18b20.h>
#include <esp_tim.h>
#include <esp_eb.h>
#include <mem.h>
/**
* Decode temperature.
*
* @param sp The address for first scrachpad byte.
*
* @return The temperature.
*/
float ICACHE_FLASH_ATTR
decode_temp(const uint8_t *sp)
{
bool minus;
uint8_t integer = 0;
float decimal = 0;
// Get only bits responsible for temperature and sign.
int16_t temp = sp[0] | (sp[1] << 8);
minus = (temp & 0x8000) > 0;
// Detect negative number.
if (minus) {
temp = (int16_t) (~temp + 1); // 2's complement.
}
// Remove fraction.
integer = (uint8_t) (temp >> 4);
// take only 3 LSB form MSB.
integer |= ((temp >> 8) & 0x07) << 4;
// Calculate fraction accounting for resolution.
switch ((sp[4] & 0x60) >> 5) {
case ESP_DS18B20_RES_12:
decimal = (temp & 0x0F) * ((float) ESP_DS18B20_STEP_12);
break;
case ESP_DS18B20_RES_11:
decimal = ((temp >> 1) & 0x07) * ((float) ESP_DS18B20_STEP_11);
break;
case ESP_DS18B20_RES_10:
decimal = ((temp >> 2) & 0x03) * ((float) ESP_DS18B20_STEP_10);
break;
case ESP_DS18B20_RES_9:
decimal = ((temp >> 3) & 0x01) * ((float) ESP_DS18B20_STEP_9);
break;
default:
decimal = 0;
}
decimal = integer + decimal;
if (minus) decimal = 0 - decimal;
return decimal;
}
esp_ow_err ICACHE_FLASH_ATTR
esp_d18b20_read_sp(esp_ow_device *device)
{
uint8_t idx;
uint8_t crc = 0;
esp_ds18b20_st *st = device->custom;
if (esp_ow_reset(device->gpio_num) == false) {
st->retries = -1;
return ESP_OW_ERR_NO_DEV;
}
esp_ow_match_dev(device);
esp_ow_write(device->gpio_num, ESP_DS18B20_CMD_READ_SP);
esp_ow_read_bytes(device->gpio_num, st->sp, 9);
for (idx = 0; idx < 9; idx++) {
crc = esp_ow_crc8(crc, st->sp[idx]);
}
if (crc != 0) {
memset(st->sp, 0, 9);
return ESP_OW_ERR_BAD_CRC;
}
return ESP_OW_OK;
}
esp_ow_err ICACHE_FLASH_ATTR
esp_ds18b20_write_sp(esp_ow_device *device)
{
uint8_t *start;
esp_ds18b20_st *st = device->custom;
if (esp_ow_reset(device->gpio_num) == false) {
return ESP_OW_ERR_NO_DEV;
}
esp_ow_match_dev(device);
esp_ow_write(device->gpio_num, ESP_DS18B20_CMD_WRITE_SP);
// We transmit only 3 bytes (Th, Tl, cfg).
start = &st->sp[2];
esp_ow_write_bytes(device->gpio_num, start, 3);
return ESP_OW_OK;
}
esp_ow_err ICACHE_FLASH_ATTR
read_temp(esp_ow_device *device)
{
esp_ds18b20_st *st = device->custom;
esp_ow_err err = esp_d18b20_read_sp(device);
st->retries = -1;
if (err != ESP_OW_OK) return err;
st->last_temp = decode_temp(st->sp);
return ESP_OW_OK;
}
static void ICACHE_FLASH_ATTR
start_conversion(void *arg)
{
esp_tim_timer *timer = arg;
bool done = false;
esp_ow_device *dev = timer->payload;
esp_ds18b20_st *st = dev->custom;
uint32_t sample_count = 200;
st->retries++;
do {
if (esp_ow_read_bit(dev->gpio_num)) {
done = true;
break;
}
os_delay_us(5);
sample_count--;
} while (sample_count > 0);
if (done == true) {
if (read_temp(dev) != ESP_OW_OK) {
esp_eb_trigger(ESP_DS18B20_EV_TEMP_ERROR, dev);
} else {
esp_eb_trigger(ESP_DS18B20_EV_TEMP_READY, dev);
}
} else {
// The worst case from datasheet is 750ms when
// 12 bits of resolution is set.
// Each call to this function takes about 200*5us (1ms).
// If temperature is not available we call ourselves again
// after 10ms. That gives 11ms per call. 68 calls * 11ms = 748ms.
// This condition basically makes sure we are not
// calling ourselves forever.
if (st->retries > 68) {
st->retries = -1;
esp_eb_trigger(ESP_DS18B20_EV_TEMP_ERROR, dev);
} else {
// Try again.
esp_tim_continue(timer);
}
}
}
bool ICACHE_FLASH_ATTR
esp_ds18b20_init(uint8_t gpio_num)
{
esp_ow_init(gpio_num);
return true;
}
esp_ow_err ICACHE_FLASH_ATTR
esp_ds18b20_search(uint8_t gpio_num, bool in_alert, esp_ow_device **list)
{
esp_ow_device *curr;
esp_ow_cmd cmd = in_alert ? ESP_OW_CMD_SEARCH_ROM_ALERT : ESP_OW_CMD_SEARCH_ROM;
esp_ow_err err = esp_ow_search_family(gpio_num, cmd, ESP_DS18B20_FAMILY_CODE, list);
if (err != ESP_OW_OK) return err;
curr = *list;
while (curr) {
curr->custom = os_zalloc(sizeof(esp_ds18b20_st));
((esp_ds18b20_st *) curr->custom)->last_temp = ESP_DS18B20_TEMP_ERR;
((esp_ds18b20_st *) curr->custom)->retries = -1;
curr = curr->next;
}
return err;
}
esp_ow_device *ICACHE_FLASH_ATTR
esp_ds18b20_new_dev(uint8_t *rom)
{
esp_ow_device *device = os_zalloc(sizeof(esp_ow_device));
esp_ds18b20_st *st = os_zalloc(sizeof(esp_ds18b20_st));
os_memcpy(device->rom, rom, 8);
st->last_temp = ESP_DS18B20_TEMP_ERR;
st->retries = -1;
device->custom = st;
return device;
}
esp_ow_device *ICACHE_FLASH_ATTR
esp_ds18b20_get(uint8_t gpio_num)
{
return esp_ow_read_rom_dev(gpio_num);
}
esp_ow_err ICACHE_FLASH_ATTR
esp_ds18b20_get_alarm(esp_ow_device *dev, int8_t *low, int8_t *high)
{
esp_ds18b20_st *st = dev->custom;
esp_ow_err err = esp_d18b20_read_sp(dev);
if (err != ESP_OW_OK) return err;
*low = st->sp[3];
*high = st->sp[2];
return ESP_OW_OK;
}
esp_ow_err ICACHE_FLASH_ATTR
esp_ds18b20_set_alarm(esp_ow_device *dev, int8_t low, int8_t high)
{
esp_ds18b20_st *st = dev->custom;
esp_ow_err err = esp_d18b20_read_sp(dev);
if (err != ESP_OW_OK) return err;
st->sp[3] = low;
st->sp[2] = high;
return esp_ds18b20_write_sp(dev);
}
void ICACHE_FLASH_ATTR
esp_ds18b20_free_list(esp_ow_device *list)
{
esp_ow_free_device_list(list, true);
}
esp_ds18b20_err ICACHE_FLASH_ATTR
esp_ds18b20_convert(esp_ow_device *device)
{
esp_ds18b20_st *st = device->custom;
// We are already waiting for the conversion.
if (st->retries >= 0) return ESP_DS18B20_ERR_CONV_IN_PROG;
if (esp_ow_reset(device->gpio_num) == false) return ESP_DS18B20_NO_DEV;
// Send conversion command.
esp_ow_match_dev(device);
esp_ow_write(device->gpio_num, ESP_DS18B20_CMD_CONVERT);
if (esp_tim_start(start_conversion, device)) {
st->retries = 0;
}
return ESP_DS18B20_OK;
}
bool ICACHE_FLASH_ATTR
esp_ds18b20_has_parasite(uint8_t gpio_num)
{
bool has_parasite;
if (esp_ow_reset(gpio_num) == false) {
return false; // No devices.
}
esp_ow_write(gpio_num, ESP_OW_CMD_SKIP_ROM);
esp_ow_write(gpio_num, ESP_DS18B20_CMD_READ_PWR);
has_parasite = !esp_ow_read_bit(gpio_num);
return has_parasite;
}

View File

@ -0,0 +1,187 @@
/*
* Copyright 2017 Rafal Zajac <rzajac@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#ifndef ESP_DS18B20_H
#define ESP_DS18B20_H
#include <esp_ow.h>
#include <c_types.h>
// The DS18B20 family code from datasheet.
#define ESP_DS18B20_FAMILY_CODE 0x28
// The absolute zero temperature is returned as error.
#define ESP_DS18B20_TEMP_ERR (-273)
// Temperature conversion ready.
#define ESP_DS18B20_EV_TEMP_READY "ds18b20tReady"
// Temperature conversion error.
#define ESP_DS18B20_EV_TEMP_ERROR "ds18b20tError"
// Temperature resolutions.
#define ESP_DS18B20_RES_9 0x0
#define ESP_DS18B20_RES_10 0x1
#define ESP_DS18B20_RES_11 0x2
#define ESP_DS18B20_RES_12 0x3
// Temperature steps.
#define ESP_DS18B20_STEP_9 0.5
#define ESP_DS18B20_STEP_10 0.25
#define ESP_DS18B20_STEP_11 0.125
#define ESP_DS18B20_STEP_12 0.0625
// OneWire commands.
typedef enum {
ESP_DS18B20_CMD_READ_PWR = 0xB4,
ESP_DS18B20_CMD_CONVERT = 0x44,
ESP_DS18B20_CMD_READ_SP = 0xBE,
ESP_DS18B20_CMD_WRITE_SP = 0x4E,
} esp_ds18b20_cmd;
typedef enum {
ESP_DS18B20_OK,
ESP_DS18B20_NO_DEV,
ESP_DS18B20_ERR_CONV_IN_PROG, // Conversion in progress.
} esp_ds18b20_err;
// DS18B20 status.
typedef struct {
uint8_t sp[9];
int8_t retries; // Is greater then zero when conversion in progress.
float last_temp; // Last successful temperature read.
} esp_ds18b20_st;
/**
* Initialize OneWire bus where DS18B20 is.
*
* To use many OneWire buses you have to initialize all of them.
*
* @param gpio_num The GPIO where OneWire bus is connected.
*
* @return Returns true on success, false otherwise.
*/
bool ICACHE_FLASH_ATTR
esp_ds18b20_init(uint8_t gpio_num);
/**
* Find devices on OneWire bus.
*
* @param in_alert Find only devices in alert mode.
* @param list The list of found devices or NULL.
*
* @return OneWire error code.
*/
esp_ow_err ICACHE_FLASH_ATTR
esp_ds18b20_search(uint8_t gpio_num, bool in_alert, esp_ow_device **list);
/**
* Construct DS18B20 device.
*
* It's up to a caller to release the memory at some point.
*
* @param rom The pointer to 8 byte ROM address
*
* @return The device.
*/
esp_ow_device *ICACHE_FLASH_ATTR
esp_ds18b20_new_dev(uint8_t *rom);
/**
* Get the only device on the bus.
*
* Can be used only when there is only one device on the OneWire bus!
*
* @param gpio_num The GPIO where OneWire bus is connected.
*
* @return The OneWire device. On error ROM address has all zeros.
*/
esp_ow_device *ICACHE_FLASH_ATTR
esp_ds18b20_get(uint8_t gpio_num);
/**
* Get DS18B20 alarm thresholds.
*
* @param dev The device to get alarm thresholds for.
* @param low The low threshold in Celsius.
* @param high The high threshold in Celsius.
*
* @return OneWire error code.
*/
esp_ow_err ICACHE_FLASH_ATTR
esp_ds18b20_get_alarm(esp_ow_device *dev, int8_t *low, int8_t *high);
/**
* Set DS18B20 alarm thresholds.
*
* @param dev The device to get alarm thresholds for.
* @param low The low threshold in Celsius.
* @param high The high threshold in Celsius.
*
* @return OneWire error code.
*/
esp_ow_err ICACHE_FLASH_ATTR
esp_ds18b20_set_alarm(esp_ow_device *dev, int8_t low, int8_t high);
/**
* Read scrachpad.
*
* @param device The device to read scratchpad for.
*
* @return OneWire error code.
*/
esp_ow_err ICACHE_FLASH_ATTR
esp_d18b20_read_sp(esp_ow_device *device);
/**
* Write scratchpad to the device.
*
* @param device The device to write scratchpad to.
*
* @return OneWire error code.
*/
esp_ow_err ICACHE_FLASH_ATTR
esp_ds18b20_write_sp(esp_ow_device *device);
/**
* Free memory allocated by devices found on OneWire bus.
*
* @param list
*/
void ICACHE_FLASH_ATTR
esp_ds18b20_free_list(esp_ow_device *list);
/**
* Start temperature conversion.
*
* @param device The device to start conversion on.
*
* @return Error code.
*/
esp_ds18b20_err ICACHE_FLASH_ATTR
esp_ds18b20_convert(esp_ow_device *device);
/**
* Check if OneWire bus has device with parasite power supply.
*
* @param gpio_num The GPIO where OneWire bus is connected.
*
* @return Returns true if parasite present, false otherwise.
*/
bool ICACHE_FLASH_ATTR
esp_ds18b20_has_parasite(uint8_t gpio_num);
#endif //ESP_DS18B20_H