add 1wire components

This commit is contained in:
2022-08-22 00:45:21 +02:00
parent 0753dce6b1
commit 937ba25b84
24 changed files with 8146 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
# Build and deploy doxygen documention to GitHub Pages
sudo: false
dist: trusty
# Blacklist
branches:
only:
- master
# Environment variables
env:
global:
- GH_REPO_REF: github.com/DavidAntliff/esp32-ds18b20.git
# Install dependencies
addons:
apt:
packages:
- doxygen
- doxygen-doc
- doxygen-latex
- doxygen-gui
- graphviz
# Build the docs
script:
- cd doc
- doxygen
# Deploy using Travis-CI/GitHub Pages integration support
deploy:
provider: pages
skip-cleanup: true
local-dir: doc/html
github-token: $GITHUB_TOKEN
on:
branch: master
target-branch: gh-pages
+6
View File
@@ -0,0 +1,6 @@
set(COMPONENT_ADD_INCLUDEDIRS include)
set(COMPONENT_SRCS "ds18b20.c")
set(COMPONENT_PRIV_REQUIRES "esp32-owb")
register_component()
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 David Antliff
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+98
View File
@@ -0,0 +1,98 @@
# esp32-ds18b20
## Introduction
This is a ESP32-compatible C component for the Maxim Integrated DS18B20 Programmable Resolution 1-Wire Digital
Thermometer device.
It supports multiple devices on the same 1-Wire bus.
It is written and tested for v2.1, v3.0-3.3 and v4.1-beta1 of the [ESP-IDF](https://github.com/espressif/esp-idf)
environment, using the xtensa-esp32-elf toolchain (gcc version 5.2.0).
## Dependencies
Requires [esp32-owb](https://github.com/DavidAntliff/esp32-owb).
## Example
See [esp32-ds18b20-example](https://github.com/DavidAntliff/esp32-ds18b20-example) for an example that supports single
and multiple devices on a single bus.
## Features
In cooperation with the underlying esp32-owb component, this component includes:
* External power supply mode.
* Parasitic power mode (VDD and GND connected) - see notes below.
* Static (stack-based) or dynamic (malloc-based) memory model.
* No globals - support any number of DS18B20 devices on any number of 1-Wire buses simultaneously.
* 1-Wire device detection and validation, including search for multiple devices on a single bus.
* Addressing optimisation for a single (solo) device on a bus.
* CRC checks on temperature data.
* Programmable temperature measurement resolution (9, 10, 11 or 12-bit resolution).
* Temperature conversion and retrieval.
* Separation of conversion and temperature retrieval to allow for simultaneous conversion across multiple devices.
## Parasitic Power Mode
Consult the [datasheet](http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf) for more detailed information about
Parasitic Power mode.
Parasitic power operation can be detected by `ds18b20_check_for_parasite_power()` followed by a call to
`owb_use_parasitic_power()`, or simply set explicitly by a call to the latter.
This library has been tested on the ESP32 with two parasitic-power configurations, with two DS18B20 devices at 3.3V:
1. Disconnect power to each device's VDD pin, and connect that pin to GND for each device. Power is supplied to
each device through the OWB data line.
2. Connect the OWB data line to VCC via a P-channel MOSFET (e.g. BS250) and current-limiting resistor (e.g. 270 Ohm).
Ensure your code calls `owb_use_strong_pullup_gpio()` with the GPIO connected to the MOSFET Gate. The GPIO will go
high during temperature conversion, turning on the MOSFET and providing power from VCC to each device through the OWB
data line.
If you set up configuration 1 and do not see CRC errors, but you get incorrect temperature readings around 80 - 85
degrees C, then it is likely that your DS18B20 devices are running out of power during the temperature conversion. In
this case, consider reducing the value of the pull-up resistor. For example, I have had success obtaining correct
conversions from two parasitic DS18B20 devices by replacing the 4k7 Ohm pull-up resistor with a 1 kOhm resistor.
Alternatively, consider using configuration 2.
Note that use of parasitic power mode disables the ability for devices on the bus to signal that an operation has
completed. This means that DS18B20 devices in parasitic power mode are not able to communicate when they have completed
a temperature conversion. In this mode, a delay for a pre-calculated duration occurs, and then the conversion result is
read from the device(s). *If your ESP32 is not running on the correct clock rate, this duration may be too short!*
## Documentation
Automatically generated API documentation (doxygen) is available [here](https://davidantliff.github.io/esp32-ds18b20/index.html).
## Source Code
The source is available from [GitHub](https://www.github.com/DavidAntliff/esp32-ds18b20).
## License
The code in this project is licensed under the MIT license - see LICENSE for details.
## Links
* [DS18B20 Datasheet](http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf)
* [1-Wire Communication Through Software](https://www.maximintegrated.com/en/app-notes/index.mvp/id/126)
* [1-Wire Search Algorithm](https://www.maximintegrated.com/en/app-notes/index.mvp/id/187)
* [Espressif IoT Development Framework for ESP32](https://github.com/espressif/esp-idf)
## Acknowledgements
Parts of this code are based on references provided to the public domain by Maxim Integrated.
"1-Wire" is a registered trademark of Maxim Integrated.
## Roadmap
The following features are anticipated but not yet implemented:
* Concurrency support (multiple tasks accessing devices on the same bus).
* Alarm support.
* EEPROM support.
* Parasitic power support.
+1
View File
@@ -0,0 +1 @@
# Use defaults
+3
View File
@@ -0,0 +1,3 @@
html/
latex/
File diff suppressed because it is too large Load Diff
+614
View File
@@ -0,0 +1,614 @@
/*
* MIT License
*
* Copyright (c) 2017-2019 David Antliff
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @file ds18b20.c
*
* Resolution is cached in the DS18B20_Info object to avoid querying the hardware
* every time a temperature conversion is required. However this can result in the
* cached value becoming inconsistent with the hardware value, so care must be taken.
*
*/
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "esp_log.h"
#include "ds18b20.h"
#include "owb.h"
static const char * TAG = "ds18b20";
static const int T_CONV = 750; // maximum conversion time at 12-bit resolution in milliseconds
// Function commands
#define DS18B20_FUNCTION_TEMP_CONVERT 0x44 ///< Initiate a single temperature conversion
#define DS18B20_FUNCTION_SCRATCHPAD_WRITE 0x4E ///< Write 3 bytes of data to the device scratchpad at positions 2, 3 and 4
#define DS18B20_FUNCTION_SCRATCHPAD_READ 0xBE ///< Read 9 bytes of data (including CRC) from the device scratchpad
#define DS18B20_FUNCTION_SCRATCHPAD_COPY 0x48 ///< Copy the contents of the scratchpad to the device EEPROM
#define DS18B20_FUNCTION_EEPROM_RECALL 0xB8 ///< Restore alarm trigger values and configuration data from EEPROM to the scratchpad
#define DS18B20_FUNCTION_POWER_SUPPLY_READ 0xB4 ///< Determine if a device is using parasitic power
/// @cond ignore
typedef struct
{
uint8_t temperature[2]; // [0] is LSB, [1] is MSB
uint8_t trigger_high;
uint8_t trigger_low;
uint8_t configuration;
uint8_t reserved[3];
uint8_t crc;
} __attribute__((packed)) Scratchpad;
/// @endcond ignore
static void _init(DS18B20_Info * ds18b20_info, const OneWireBus * bus)
{
if (ds18b20_info != NULL)
{
ds18b20_info->bus = bus;
memset(&ds18b20_info->rom_code, 0, sizeof(ds18b20_info->rom_code));
ds18b20_info->use_crc = false;
ds18b20_info->resolution = DS18B20_RESOLUTION_INVALID;
ds18b20_info->solo = false; // assume multiple devices unless told otherwise
ds18b20_info->init = true;
}
else
{
ESP_LOGE(TAG, "ds18b20_info is NULL");
}
}
static bool _is_init(const DS18B20_Info * ds18b20_info)
{
bool ok = false;
if (ds18b20_info != NULL)
{
if (ds18b20_info->init)
{
// OK
ok = true;
}
else
{
ESP_LOGE(TAG, "ds18b20_info is not initialised");
}
}
else
{
ESP_LOGE(TAG, "ds18b20_info is NULL");
}
return ok;
}
static bool _address_device(const DS18B20_Info * ds18b20_info)
{
bool present = false;
if (_is_init(ds18b20_info))
{
owb_reset(ds18b20_info->bus, &present);
if (present)
{
if (ds18b20_info->solo)
{
// if there's only one device on the bus, we can skip
// sending the ROM code and instruct it directly
owb_write_byte(ds18b20_info->bus, OWB_ROM_SKIP);
}
else
{
// if there are multiple devices on the bus, a Match ROM command
// must be issued to address a specific slave
owb_write_byte(ds18b20_info->bus, OWB_ROM_MATCH);
owb_write_rom_code(ds18b20_info->bus, ds18b20_info->rom_code);
}
}
else
{
ESP_LOGE(TAG, "ds18b20 device not responding");
}
}
return present;
}
static bool _check_resolution(DS18B20_RESOLUTION resolution)
{
return (resolution >= DS18B20_RESOLUTION_9_BIT) && (resolution <= DS18B20_RESOLUTION_12_BIT);
}
static float _wait_for_duration(DS18B20_RESOLUTION resolution)
{
int64_t start_time = esp_timer_get_time();
if (_check_resolution(resolution))
{
int divisor = 1 << (DS18B20_RESOLUTION_12_BIT - resolution);
ESP_LOGD(TAG, "divisor %d", divisor);
float max_conversion_time = (float)T_CONV / (float)divisor;
int ticks = ceil(max_conversion_time / portTICK_PERIOD_MS);
ESP_LOGD(TAG, "wait for conversion: %.3f ms, %d ticks", max_conversion_time, ticks);
// wait at least this maximum conversion time
vTaskDelay(ticks);
}
int64_t end_time = esp_timer_get_time();
return (float)(end_time - start_time) / 1000000.0f;
}
static float _wait_for_device_signal(const DS18B20_Info * ds18b20_info)
{
float elapsed_time = 0.0f;
if (_check_resolution(ds18b20_info->resolution))
{
int divisor = 1 << (DS18B20_RESOLUTION_12_BIT - ds18b20_info->resolution);
// allow for 10% overtime
float max_conversion_time = (float)T_CONV / (float)divisor * 1.1;
int max_conversion_ticks = ceil(max_conversion_time / portTICK_PERIOD_MS);
ESP_LOGD(TAG, "wait for conversion: max %.0f ms, %d ticks", max_conversion_time, max_conversion_ticks);
// wait for conversion to complete - all devices will pull bus low once complete
TickType_t start_ticks = xTaskGetTickCount();
TickType_t duration_ticks = 0;
uint8_t status = 0;
do
{
vTaskDelay(1);
owb_read_bit(ds18b20_info->bus, &status);
duration_ticks = xTaskGetTickCount() - start_ticks;
} while (status == 0 && duration_ticks < max_conversion_ticks);
elapsed_time = duration_ticks * portTICK_PERIOD_MS;
if (duration_ticks >= max_conversion_ticks)
{
ESP_LOGW(TAG, "conversion timed out");
}
else
{
ESP_LOGD(TAG, "conversion took at most %.0f ms", elapsed_time);
}
}
return elapsed_time;
}
static float _decode_temp(uint8_t lsb, uint8_t msb, DS18B20_RESOLUTION resolution)
{
float result = 0.0f;
if (_check_resolution(resolution))
{
// masks to remove undefined bits from result
static const uint8_t lsb_mask[4] = { ~0x07, ~0x03, ~0x01, ~0x00 };
uint8_t lsb_masked = lsb_mask[resolution - DS18B20_RESOLUTION_9_BIT] & lsb;
int16_t raw = (msb << 8) | lsb_masked;
result = raw / 16.0f;
}
else
{
ESP_LOGE(TAG, "Unsupported resolution %d", resolution);
}
return result;
}
static size_t _min(size_t x, size_t y)
{
return x > y ? y : x;
}
static DS18B20_ERROR _read_scratchpad(const DS18B20_Info * ds18b20_info, Scratchpad * scratchpad, size_t count)
{
// If CRC is enabled, regardless of count, read the entire scratchpad and verify the CRC,
// otherwise read up to the scratchpad size, or count, whichever is smaller.
if (!scratchpad) {
return DS18B20_ERROR_NULL;
}
DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN;
if (ds18b20_info->use_crc)
{
count = sizeof(Scratchpad);
}
count = _min(sizeof(Scratchpad), count); // avoid reading past end of scratchpad
ESP_LOGD(TAG, "scratchpad read: CRC %d, count %d", ds18b20_info->use_crc, count);
if (_address_device(ds18b20_info))
{
// read scratchpad
if (owb_write_byte(ds18b20_info->bus, DS18B20_FUNCTION_SCRATCHPAD_READ) == OWB_STATUS_OK)
{
if (owb_read_bytes(ds18b20_info->bus, (uint8_t *)scratchpad, count) == OWB_STATUS_OK)
{
ESP_LOG_BUFFER_HEX_LEVEL(TAG, scratchpad, count, ESP_LOG_DEBUG);
err = DS18B20_OK;
if (!ds18b20_info->use_crc)
{
// Without CRC, or partial read:
ESP_LOGD(TAG, "No CRC check");
bool is_present = false;
owb_reset(ds18b20_info->bus, &is_present); // terminate early
}
else
{
// With CRC:
if (owb_crc8_bytes(0, (uint8_t *)scratchpad, sizeof(*scratchpad)) != 0)
{
ESP_LOGE(TAG, "CRC failed");
err = DS18B20_ERROR_CRC;
}
else
{
ESP_LOGD(TAG, "CRC ok");
}
}
}
else
{
ESP_LOGE(TAG, "owb_read_bytes failed");
err = DS18B20_ERROR_OWB;
}
}
else
{
ESP_LOGE(TAG, "owb_write_byte failed");
err = DS18B20_ERROR_OWB;
}
}
else
{
err = DS18B20_ERROR_DEVICE;
}
return err;
}
static bool _write_scratchpad(const DS18B20_Info * ds18b20_info, const Scratchpad * scratchpad, bool verify)
{
bool result = false;
// Only bytes 2, 3 and 4 (trigger and configuration) can be written.
// All three bytes MUST be written before the next reset to avoid corruption.
if (_is_init(ds18b20_info))
{
if (_address_device(ds18b20_info))
{
ESP_LOGD(TAG, "scratchpad write 3 bytes:");
ESP_LOG_BUFFER_HEX_LEVEL(TAG, &scratchpad->trigger_high, 3, ESP_LOG_DEBUG);
owb_write_byte(ds18b20_info->bus, DS18B20_FUNCTION_SCRATCHPAD_WRITE);
owb_write_bytes(ds18b20_info->bus, (uint8_t *)&scratchpad->trigger_high, 3);
result = true;
if (verify)
{
Scratchpad read = {0};
if (_read_scratchpad(ds18b20_info, &read, offsetof(Scratchpad, configuration) + 1) == DS18B20_OK)
{
if (memcmp(&scratchpad->trigger_high, &read.trigger_high, 3) != 0)
{
ESP_LOGE(TAG, "scratchpad verify failed: "
"wrote {0x%02x, 0x%02x, 0x%02x}, "
"read {0x%02x, 0x%02x, 0x%02x}",
scratchpad->trigger_high, scratchpad->trigger_low, scratchpad->configuration,
read.trigger_high, read.trigger_low, read.configuration);
result = false;
}
}
else
{
ESP_LOGE(TAG, "read scratchpad failed");
result = false;
}
}
}
}
return result;
}
// Public API
DS18B20_Info * ds18b20_malloc(void)
{
DS18B20_Info * ds18b20_info = malloc(sizeof(*ds18b20_info));
if (ds18b20_info != NULL)
{
memset(ds18b20_info, 0, sizeof(*ds18b20_info));
ESP_LOGD(TAG, "malloc %p", ds18b20_info);
}
else
{
ESP_LOGE(TAG, "malloc failed");
}
return ds18b20_info;
}
void ds18b20_free(DS18B20_Info ** ds18b20_info)
{
if (ds18b20_info != NULL && (*ds18b20_info != NULL))
{
ESP_LOGD(TAG, "free %p", *ds18b20_info);
free(*ds18b20_info);
*ds18b20_info = NULL;
}
}
void ds18b20_init(DS18B20_Info * ds18b20_info, const OneWireBus * bus, OneWireBus_ROMCode rom_code)
{
if (ds18b20_info != NULL)
{
_init(ds18b20_info, bus);
ds18b20_info->rom_code = rom_code;
// read current resolution from device as it may not be power-on or factory default
ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info);
}
else
{
ESP_LOGE(TAG, "ds18b20_info is NULL");
}
}
void ds18b20_init_solo(DS18B20_Info * ds18b20_info, const OneWireBus * bus)
{
if (ds18b20_info != NULL)
{
_init(ds18b20_info, bus);
ds18b20_info->solo = true;
// ROM code not required
// read current resolution from device as it may not be power-on or factory default
ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info);
}
else
{
ESP_LOGE(TAG, "ds18b20_info is NULL");
}
}
void ds18b20_use_crc(DS18B20_Info * ds18b20_info, bool use_crc)
{
if (_is_init(ds18b20_info))
{
ds18b20_info->use_crc = use_crc;
ESP_LOGD(TAG, "use_crc %d", ds18b20_info->use_crc);
}
}
bool ds18b20_set_resolution(DS18B20_Info * ds18b20_info, DS18B20_RESOLUTION resolution)
{
bool result = false;
if (_is_init(ds18b20_info))
{
if (_check_resolution(ds18b20_info->resolution))
{
// read scratchpad up to and including configuration register
Scratchpad scratchpad = {0};
_read_scratchpad(ds18b20_info, &scratchpad,
offsetof(Scratchpad, configuration) - offsetof(Scratchpad, temperature) + 1);
// modify configuration register to set resolution
uint8_t value = (((resolution - 1) & 0x03) << 5) | 0x1f;
scratchpad.configuration = value;
ESP_LOGD(TAG, "configuration value 0x%02x", value);
// write bytes 2, 3 and 4 of scratchpad
result = _write_scratchpad(ds18b20_info, &scratchpad, /* verify */ true);
if (result)
{
ds18b20_info->resolution = resolution;
ESP_LOGD(TAG, "Resolution set to %d bits", (int)resolution);
}
else
{
// Resolution change failed - update the info resolution with the value read from configuration
ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info);
ESP_LOGW(TAG, "Resolution consistency lost - refreshed from device: %d", ds18b20_info->resolution);
}
}
else
{
ESP_LOGE(TAG, "Unsupported resolution %d", resolution);
}
}
return result;
}
DS18B20_RESOLUTION ds18b20_read_resolution(DS18B20_Info * ds18b20_info)
{
DS18B20_RESOLUTION resolution = DS18B20_RESOLUTION_INVALID;
if (_is_init(ds18b20_info))
{
// read scratchpad up to and including configuration register
Scratchpad scratchpad = {0};
_read_scratchpad(ds18b20_info, &scratchpad,
offsetof(Scratchpad, configuration) - offsetof(Scratchpad, temperature) + 1);
resolution = ((scratchpad.configuration >> 5) & 0x03) + DS18B20_RESOLUTION_9_BIT;
if (!_check_resolution(resolution))
{
ESP_LOGE(TAG, "invalid resolution read from device: 0x%02x", scratchpad.configuration);
resolution = DS18B20_RESOLUTION_INVALID;
}
else
{
ESP_LOGD(TAG, "Resolution read as %d", resolution);
}
}
return resolution;
}
bool ds18b20_convert(const DS18B20_Info * ds18b20_info)
{
bool result = false;
if (_is_init(ds18b20_info))
{
const OneWireBus * bus = ds18b20_info->bus;
if (_address_device(ds18b20_info))
{
// initiate a temperature measurement
owb_write_byte(bus, DS18B20_FUNCTION_TEMP_CONVERT);
result = true;
}
else
{
ESP_LOGE(TAG, "ds18b20 device not responding");
}
}
return result;
}
void ds18b20_convert_all(const OneWireBus * bus)
{
if (bus)
{
bool is_present = false;
owb_reset(bus, &is_present);
owb_write_byte(bus, OWB_ROM_SKIP);
owb_write_byte(bus, DS18B20_FUNCTION_TEMP_CONVERT);
owb_set_strong_pullup(bus, true);
}
else
{
ESP_LOGE(TAG, "bus is NULL");
}
}
float ds18b20_wait_for_conversion(const DS18B20_Info * ds18b20_info)
{
float elapsed_time = 0.0f;
if (_is_init(ds18b20_info))
{
if (ds18b20_info->bus->use_parasitic_power)
{
// in parasitic mode, devices cannot signal when they are complete,
// so use the datasheet values to wait for a duration.
elapsed_time = _wait_for_duration(ds18b20_info->resolution);
}
else
{
// wait for the device(s) to indicate the conversion is complete
elapsed_time = _wait_for_device_signal(ds18b20_info);
}
}
return elapsed_time;
}
DS18B20_ERROR ds18b20_read_temp(const DS18B20_Info * ds18b20_info, float * value)
{
DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN;
if (_is_init(ds18b20_info))
{
uint8_t temp_LSB = 0x00;
uint8_t temp_MSB = 0x80;
Scratchpad scratchpad = {0};
if ((err = _read_scratchpad(ds18b20_info, &scratchpad, 2)) == DS18B20_OK)
{
temp_LSB = scratchpad.temperature[0];
temp_MSB = scratchpad.temperature[1];
}
// https://github.com/cpetrich/counterfeit_DS18B20#solution-to-the-85-c-problem
if (scratchpad.reserved[1] == 0x0c && temp_MSB == 0x05 && temp_LSB == 0x50)
{
ESP_LOGE(TAG, "Read power-on value (85.0)");
err = DS18B20_ERROR_DEVICE;
}
float temp = _decode_temp(temp_LSB, temp_MSB, ds18b20_info->resolution);
ESP_LOGD(TAG, "temp_LSB 0x%02x, temp_MSB 0x%02x, temp %f", temp_LSB, temp_MSB, temp);
if (value)
{
*value = temp;
}
}
return err;
}
DS18B20_ERROR ds18b20_convert_and_read_temp(const DS18B20_Info * ds18b20_info, float * value)
{
DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN;
if (_is_init(ds18b20_info))
{
if (ds18b20_convert(ds18b20_info))
{
// wait at least maximum conversion time
ds18b20_wait_for_conversion(ds18b20_info);
if (value)
{
*value = 0.0f;
err = ds18b20_read_temp(ds18b20_info, value);
}
else
{
err = DS18B20_ERROR_NULL;
}
}
}
return err;
}
DS18B20_ERROR ds18b20_check_for_parasite_power(const OneWireBus * bus, bool * present)
{
DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN;
ESP_LOGD(TAG, "ds18b20_check_for_parasite_power");
if (bus) {
bool reset_present;
if ((err = owb_reset(bus, &reset_present)) == DS18B20_OK)
{
ESP_LOGD(TAG, "owb_reset OK");
if ((err = owb_write_byte(bus, OWB_ROM_SKIP)) == DS18B20_OK)
{
ESP_LOGD(TAG, "owb_write_byte(ROM_SKIP) OK");
if ((err = owb_write_byte(bus, DS18B20_FUNCTION_POWER_SUPPLY_READ)) == DS18B20_OK)
{
// Parasitic-powered devices will pull the bus low during read time slot
ESP_LOGD(TAG, "owb_write_byte(POWER_SUPPLY_READ) OK");
uint8_t value = 0;
if ((err = owb_read_bit(bus, &value)) == DS18B20_OK)
{
ESP_LOGD(TAG, "owb_read_bit OK: 0x%02x", value);
if (present)
{
*present = !(bool)(value & 0x01u);
}
}
}
}
}
}
else
{
ESP_LOGE(TAG, "bus is NULL");
err = DS18B20_ERROR_NULL;
}
return err;
}
+206
View File
@@ -0,0 +1,206 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @file ds18b20.h
* @brief Interface definitions for the Maxim Integrated DS18B20 Programmable
* Resolution 1-Wire Digital Thermometer device.
*
* This component provides structures and functions that are useful for communicating
* with DS18B20 devices connected via a Maxim Integrated 1-Wire® bus.
*/
#ifndef DS18B20_H
#define DS18B20_H
#include "owb.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Success and error codes.
*/
typedef enum
{
DS18B20_ERROR_UNKNOWN = -1, ///< An unknown error occurred, or the value was not set
DS18B20_OK = 0, ///< Success
DS18B20_ERROR_DEVICE, ///< A device error occurred
DS18B20_ERROR_CRC, ///< A CRC error occurred
DS18B20_ERROR_OWB, ///< A One Wire Bus error occurred
DS18B20_ERROR_NULL, ///< A parameter or value is NULL
} DS18B20_ERROR;
/**
* @brief Symbols for the supported temperature resolution of the device.
*/
typedef enum
{
DS18B20_RESOLUTION_INVALID = -1, ///< Invalid resolution
DS18B20_RESOLUTION_9_BIT = 9, ///< 9-bit resolution, LSB bits 2,1,0 undefined
DS18B20_RESOLUTION_10_BIT = 10, ///< 10-bit resolution, LSB bits 1,0 undefined
DS18B20_RESOLUTION_11_BIT = 11, ///< 11-bit resolution, LSB bit 0 undefined
DS18B20_RESOLUTION_12_BIT = 12, ///< 12-bit resolution (default)
} DS18B20_RESOLUTION;
/**
* @brief Structure containing information related to a single DS18B20 device connected
* via a 1-Wire bus.
*/
typedef struct
{
bool init; ///< True if struct has been initialised, otherwise false
bool solo; ///< True if device is intended to be the only one connected to the bus, otherwise false
bool use_crc; ///< True if CRC checks are to be used when retrieving information from a device on the bus
const OneWireBus * bus; ///< Pointer to 1-Wire bus information relevant to this device
OneWireBus_ROMCode rom_code; ///< The ROM code used to address this device on the bus
DS18B20_RESOLUTION resolution; ///< Temperature measurement resolution per reading
} DS18B20_Info;
/**
* @brief Construct a new device info instance.
* New instance should be initialised before calling other functions.
* @return Pointer to new device info instance, or NULL if it cannot be created.
*/
DS18B20_Info * ds18b20_malloc(void);
/**
* @brief Delete an existing device info instance.
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in,out] ds18b20_info Pointer to device info instance that will be freed and set to NULL.
*/
void ds18b20_free(DS18B20_Info ** ds18b20_info);
/**
* @brief Initialise a device info instance with the specified GPIO.
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in] bus Pointer to initialised 1-Wire bus instance.
* @param[in] rom_code Device-specific ROM code to identify a device on the bus.
*/
void ds18b20_init(DS18B20_Info * ds18b20_info, const OneWireBus * bus, OneWireBus_ROMCode rom_code);
/**
* @brief Initialise a device info instance as a solo device on the bus.
*
* This is subject to the requirement that this device is the ONLY device on the bus.
* This allows for faster commands to be used without ROM code addressing.
*
* NOTE: if additional devices are added to the bus, operation will cease to work correctly.
*
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in] bus Pointer to initialised 1-Wire bus instance.
*/
void ds18b20_init_solo(DS18B20_Info * ds18b20_info, const OneWireBus * bus);
/**
* @brief Enable or disable use of CRC checks on device communications.
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in] use_crc True to enable CRC checks, false to disable.
*/
void ds18b20_use_crc(DS18B20_Info * ds18b20_info, bool use_crc);
/**
* @brief Set temperature measurement resolution.
*
* This programs the hardware to the specified resolution and sets the cached value to be the same.
* If the program fails, the value currently in hardware is used to refresh the cache.
*
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in] resolution Selected resolution.
* @return True if successful, otherwise false.
*/
bool ds18b20_set_resolution(DS18B20_Info * ds18b20_info, DS18B20_RESOLUTION resolution);
/**
* @brief Update and return the current temperature measurement resolution from the device.
* @param[in] ds18b20_info Pointer to device info instance.
* @return The currently configured temperature measurement resolution.
*/
DS18B20_RESOLUTION ds18b20_read_resolution(DS18B20_Info * ds18b20_info);
/**
* @brief Read 64-bit ROM code from device - only works when there is a single device on the bus.
* @param[in] ds18b20_info Pointer to device info instance.
* @return The 64-bit value read from the device's ROM.
*/
OneWireBus_ROMCode ds18b20_read_rom(DS18B20_Info * ds18b20_info);
/**
* @brief Start a temperature measurement conversion on a single device.
* @param[in] ds18b20_info Pointer to device info instance.
*/
bool ds18b20_convert(const DS18B20_Info * ds18b20_info);
/**
* @brief Start temperature conversion on all connected devices.
*
* This should be followed by a sufficient delay to ensure all devices complete
* their conversion before the measurements are read.
* @param[in] bus Pointer to initialised bus instance.
*/
void ds18b20_convert_all(const OneWireBus * bus);
/**
* @brief Wait for the maximum conversion time according to the current resolution of the device.
* In external power mode, the device or devices can signal when conversion has completed.
* In parasitic power mode, this is not possible, so a pre-calculated delay is performed.
* @param[in] ds18b20_info Pointer to device info instance.
* @return An estimate of the time elapsed, in milliseconds. Actual elapsed time may be greater.
*/
float ds18b20_wait_for_conversion(const DS18B20_Info * ds18b20_info);
/**
* @brief Read last temperature measurement from device.
*
* This is typically called after ds18b20_start_mass_conversion(), provided enough time
* has elapsed to ensure that all devices have completed their conversions.
* @param[in] ds18b20_info Pointer to device info instance. Must be initialised first.
* @param[out] value Pointer to the measurement value returned by the device, in degrees Celsius.
* @return DS18B20_OK if read is successful, otherwise error.
*/
DS18B20_ERROR ds18b20_read_temp(const DS18B20_Info * ds18b20_info, float * value);
/**
* @brief Convert, wait and read current temperature from device.
* @param[in] ds18b20_info Pointer to device info instance. Must be initialised first.
* @param[out] value Pointer to the measurement value returned by the device, in degrees Celsius.
* @return DS18B20_OK if read is successful, otherwise error.
*/
DS18B20_ERROR ds18b20_convert_and_read_temp(const DS18B20_Info * ds18b20_info, float * value);
/**
* @brief Check OneWire bus for presence of parasitic-powered devices.
*
* @param[in] bus Pointer to initialised bus instance.
* @param[out] present Result value, true if a parasitic-powered device was detected.
* @return DS18B20_OK if check is successful, otherwise error.
*/
DS18B20_ERROR ds18b20_check_for_parasite_power(const OneWireBus * bus, bool * present);
#ifdef __cplusplus
}
#endif
#endif // DS18B20_H
+26
View File
@@ -0,0 +1,26 @@
{
"name": "esp32-ds18b20",
"keywords": "onewire, 1-wire, bus, sensor, temperature",
"description": "ESP32-compatible C component for the Maxim Integrated DS18B20 Programmable Resolution 1-Wire Digital Thermometer.",
"repository":
{
"type": "git",
"url": "https://github.com/DavidAntliff/esp32-ds18b20.git"
},
"authors":
[
{
"name": "David Antliff",
"url": "https://github.com/DavidAntliff",
"maintainer": true
}
],
"version": "0.1",
"frameworks": "espidf",
"platforms": "espressif32",
"build": {
"flags": [
"-I include/"
]
}
}