forked from electro/esp-irblaster
add 1wire components
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
set(COMPONENT_ADD_INCLUDEDIRS include)
|
||||
set(COMPONENT_SRCS "ds18b20.c")
|
||||
set(COMPONENT_PRIV_REQUIRES "esp32-owb")
|
||||
register_component()
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1 @@
|
||||
# Use defaults
|
||||
@@ -0,0 +1,3 @@
|
||||
html/
|
||||
latex/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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/"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user