add 1wire components

master
Ondřej Hruška 2 years ago
parent 0753dce6b1
commit 937ba25b84
  1. 39
      components/esp32-ds18b20/.travis.yml
  2. 6
      components/esp32-ds18b20/CMakeLists.txt
  3. 21
      components/esp32-ds18b20/LICENSE
  4. 98
      components/esp32-ds18b20/README.md
  5. 1
      components/esp32-ds18b20/component.mk
  6. 3
      components/esp32-ds18b20/doc/.gitignore
  7. 2496
      components/esp32-ds18b20/doc/Doxyfile
  8. 614
      components/esp32-ds18b20/ds18b20.c
  9. 206
      components/esp32-ds18b20/include/ds18b20.h
  10. 26
      components/esp32-ds18b20/library.json
  11. 39
      components/esp32-owb/.travis.yml
  12. 5
      components/esp32-owb/CMakeLists.txt
  13. 21
      components/esp32-owb/LICENSE
  14. 60
      components/esp32-owb/README.md
  15. 1
      components/esp32-owb/component.mk
  16. 3
      components/esp32-owb/doc/.gitignore
  17. 2496
      components/esp32-owb/doc/Doxyfile
  18. 336
      components/esp32-owb/include/owb.h
  19. 70
      components/esp32-owb/include/owb_gpio.h
  20. 75
      components/esp32-owb/include/owb_rmt.h
  21. 30
      components/esp32-owb/library.json
  22. 744
      components/esp32-owb/owb.c
  23. 287
      components/esp32-owb/owb_gpio.c
  24. 469
      components/esp32-owb/owb_rmt.c

@ -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,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/"
]
}
}

@ -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-owb.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,5 @@
set(COMPONENT_ADD_INCLUDEDIRS include)
set(COMPONENT_SRCS "owb.c" "owb_gpio.c" "owb_rmt.c")
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,60 @@
# esp32-owb
This is a ESP32-compatible C component for the Maxim Integrated "1-Wire" protocol.
It is written and tested for version 3.0-3.3 and 4.1-beta1 of the [ESP-IDF](https://github.com/espressif/esp-idf)
environment, using the xtensa-esp32-elf toolchain (gcc version 5.2.0, crosstool-ng-1.22.0-80-g6c4433a).
Support for v2.1 is available on the [ESP-IDF_v2.1](https://github.com/DavidAntliff/esp32-owb/tree/ESP-IDF_v2.1) branch.
## Features
This library includes:
* External power supply mode.
* Parasitic power mode.
* Static (stack-based) or dynamic (malloc-based) memory model.
* No globals - support 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.
* 1-Wire bus operations including multi-byte read and write operations.
* CRC checks on ROM code.
This component includes two methods of bus access - delay-driven GPIO and RMT-driven slots.
The original implementation used CPU delays to construct the 1-Wire read/write timeslots
however this proved to be too unreliable. A second method, using the ESP32's RMT peripheral,
results in very accurate read/write timeslots and more reliable operation.
Therefore I highly recommend that you use the RMT driver. *The GPIO driver is deprecated and will be removed.*
See documentation for [esp32-ds18b20](https://www.github.com/DavidAntliff/esp32-ds18b20-example#parasitic-power-mode)
for further information about parasitic power mode, including strong pull-up configuration.
## Documentation
Automatically generated API documentation (doxygen) is available [here](https://davidantliff.github.io/esp32-owb/index.html).
## Source Code
The source is available from [GitHub](https://www.github.com/DavidAntliff/esp32-owb).
## License
The code in this project is licensed under the MIT license - see LICENSE for details.
## Links
* [esp32-ds18b20](https://github.com/DavidAntliff/esp32-ds18b20) - ESP32-compatible DS18B20 Digital Thermometer
component for ESP32
* [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
Thank you to [Chris Morgan](https://github.com/chmorgan) for his contribution of adding RMT peripheral support for more
reliable operation.
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.

@ -0,0 +1,3 @@
html/
latex/

File diff suppressed because it is too large Load Diff

@ -0,0 +1,336 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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
* @brief Interface definitions for the 1-Wire bus component.
*
* This component provides structures and functions that are useful for communicating
* with devices connected to a Maxim Integrated 1-Wire® bus via a single GPIO.
*
* Externally powered and "parasite-powered" devices are supported.
* Please consult your device's datasheet for power requirements.
*/
#ifndef ONE_WIRE_BUS_H
#define ONE_WIRE_BUS_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "driver/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
// ROM commands
#define OWB_ROM_SEARCH 0xF0 ///< Perform Search ROM cycle to identify devices on the bus
#define OWB_ROM_READ 0x33 ///< Read device ROM (single device on bus only)
#define OWB_ROM_MATCH 0x55 ///< Address a specific device on the bus by ROM
#define OWB_ROM_SKIP 0xCC ///< Address all devices on the bus simultaneously
#define OWB_ROM_SEARCH_ALARM 0xEC ///< Address all devices on the bus with a set alarm flag
#define OWB_ROM_CODE_STRING_LENGTH (17) ///< Typical length of OneWire bus ROM ID as ASCII hex string, including null terminator
#ifndef GPIO_NUM_NC
# define GPIO_NUM_NC (-1) ///< ESP-IDF prior to v4.x does not define GPIO_NUM_NC
#endif
struct owb_driver;
/**
* @brief Structure containing 1-Wire bus information relevant to a single instance.
*/
typedef struct
{
const struct _OneWireBus_Timing * timing; ///< Pointer to timing information
bool use_crc; ///< True if CRC checks are to be used when retrieving information from a device on the bus
bool use_parasitic_power; ///< True if parasitic-powered devices are expected on the bus
gpio_num_t strong_pullup_gpio; ///< Set if an external strong pull-up circuit is required
const struct owb_driver * driver; ///< Pointer to hardware driver instance
} OneWireBus;
/**
* @brief Represents a 1-Wire ROM Code. This is a sequence of eight bytes, where
* the first byte is the family number, then the following 6 bytes form the
* serial number. The final byte is the CRC8 check byte.
*/
typedef union
{
/// Provides access via field names
struct fields
{
uint8_t family[1]; ///< family identifier (1 byte, LSB - read/write first)
uint8_t serial_number[6]; ///< serial number (6 bytes)
uint8_t crc[1]; ///< CRC check byte (1 byte, MSB - read/write last)
} fields; ///< Provides access via field names
uint8_t bytes[8]; ///< Provides raw byte access
} OneWireBus_ROMCode;
/**
* @brief Represents the state of a device search on the 1-Wire bus.
*
* Pass a pointer to this structure to owb_search_first() and
* owb_search_next() to iterate through detected devices on the bus.
*/
typedef struct
{
OneWireBus_ROMCode rom_code; ///< Device ROM code
int last_discrepancy; ///< Bit index that identifies from which bit the next search discrepancy check should start
int last_family_discrepancy; ///< Bit index that identifies the last discrepancy within the first 8-bit family code of the ROM code
int last_device_flag; ///< Flag to indicate previous search was the last device detected
} OneWireBus_SearchState;
/**
* @brief Represents the result of OWB API functions.
*/
typedef enum
{
OWB_STATUS_NOT_SET = -1, ///< A status value has not been set
OWB_STATUS_OK = 0, ///< Operation succeeded
OWB_STATUS_NOT_INITIALIZED, ///< Function was passed an uninitialised variable
OWB_STATUS_PARAMETER_NULL, ///< Function was passed a null pointer
OWB_STATUS_DEVICE_NOT_RESPONDING, ///< No response received from the addressed device or devices
OWB_STATUS_CRC_FAILED, ///< CRC failed on data received from a device or devices
OWB_STATUS_TOO_MANY_BITS, ///< Attempt to write an incorrect number of bits to the One Wire Bus
OWB_STATUS_HW_ERROR ///< A hardware error occurred
} owb_status;
/** NOTE: Driver assumes that (*init) was called prior to any other methods */
struct owb_driver
{
/** Driver identification **/
const char* name;
/** Pointer to driver uninitialization function **/
owb_status (*uninitialize)(const OneWireBus * bus);
/** Pointer to driver reset functio **/
owb_status (*reset)(const OneWireBus * bus, bool *is_present);
/** NOTE: The data is shifted out of the low bits, eg. it is written in the order of lsb to msb */
owb_status (*write_bits)(const OneWireBus *bus, uint8_t out, int number_of_bits_to_write);
/** NOTE: Data is read into the high bits, eg. each bit read is shifted down before the next bit is read */
owb_status (*read_bits)(const OneWireBus *bus, uint8_t *in, int number_of_bits_to_read);
};
/// @cond ignore
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/// @endcond
/**
* @brief call to release resources after completing use of the OneWireBus
* @param[in] bus Pointer to initialised bus instance.
* @return status
*/
owb_status owb_uninitialize(OneWireBus * bus);
/**
* @brief Enable or disable use of CRC checks on device communications.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] use_crc True to enable CRC checks, false to disable.
* @return status
*/
owb_status owb_use_crc(OneWireBus * bus, bool use_crc);
/**
* @brief Enable or disable use of parasitic power on the One Wire Bus.
* This affects how devices signal on the bus, as devices cannot
* signal by pulling the bus low when it is pulled high.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] use_parasitic_power True to enable parasitic power, false to disable.
* @return status
*/
owb_status owb_use_parasitic_power(OneWireBus * bus, bool use_parasitic_power);
/**
* @brief Enable or disable use of extra GPIO to activate strong pull-up circuit.
* This only has effect if parasitic power mode is enabled.
* signal by pulling the bus low when it is pulled high.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] gpio Set to GPIO number to use, or GPIO_NUM_NC to disable.
* @return status
*/
owb_status owb_use_strong_pullup_gpio(OneWireBus * bus, gpio_num_t gpio);
/**
* @brief Read ROM code from device - only works when there is a single device on the bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] rom_code the value read from the device's rom
* @return status
*/
owb_status owb_read_rom(const OneWireBus * bus, OneWireBus_ROMCode * rom_code);
/**
* @brief Verify the device specified by ROM code is present.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] rom_code ROM code to verify.
* @param[out] is_present Set to true if a device is present, false if not
* @return status
*/
owb_status owb_verify_rom(const OneWireBus * bus, OneWireBus_ROMCode rom_code, bool * is_present);
/**
* @brief Reset the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] is_present set to true if at least one device is present on the bus
* @return status
*/
owb_status owb_reset(const OneWireBus * bus, bool * is_present);
/**
* @brief Read a single bit from the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] out The bit value read from the bus.
* @return status
*/
owb_status owb_read_bit(const OneWireBus * bus, uint8_t * out);
/**
* @brief Read a single byte from the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] out The byte value read from the bus (lsb only).
* @return status
*/
owb_status owb_read_byte(const OneWireBus * bus, uint8_t * out);
/**
* @brief Read a number of bytes from the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in, out] buffer Pointer to buffer to receive read data.
* @param[in] len Number of bytes to read, must not exceed length of receive buffer.
* @return status.
*/
owb_status owb_read_bytes(const OneWireBus * bus, uint8_t * buffer, unsigned int len);
/**
* @brief Write a bit to the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] bit Value to write (lsb only).
* @return status
*/
owb_status owb_write_bit(const OneWireBus * bus, uint8_t bit);
/**
* @brief Write a single byte to the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] data Byte value to write to bus.
* @return status
*/
owb_status owb_write_byte(const OneWireBus * bus, uint8_t data);
/**
* @brief Write a number of bytes to the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] buffer Pointer to buffer to write data from.
* @param[in] len Number of bytes to write.
* @return status
*/
owb_status owb_write_bytes(const OneWireBus * bus, const uint8_t * buffer, size_t len);
/**
* @brief Write a ROM code to the 1-Wire bus ensuring LSB is sent first.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] rom_code ROM code to write to bus.
* @return status
*/
owb_status owb_write_rom_code(const OneWireBus * bus, OneWireBus_ROMCode rom_code);
/**
* @brief 1-Wire 8-bit CRC lookup.
* @param[in] crc Starting CRC value. Pass in prior CRC to accumulate.
* @param[in] data Byte to feed into CRC.
* @return Resultant CRC value.
* Should be zero if last byte was the CRC byte and the CRC matches.
*/
uint8_t owb_crc8_byte(uint8_t crc, uint8_t data);
/**
* @brief 1-Wire 8-bit CRC lookup with accumulation over a block of bytes.
* @param[in] crc Starting CRC value. Pass in prior CRC to accumulate.
* @param[in] data Array of bytes to feed into CRC.
* @param[in] len Length of data array in bytes.
* @return Resultant CRC value.
* Should be zero if last byte was the CRC byte and the CRC matches.
*/
uint8_t owb_crc8_bytes(uint8_t crc, const uint8_t * data, size_t len);
/**
* @brief Locates the first device on the 1-Wire bus, if present.
* @param[in] bus Pointer to initialised bus instance.
* @param[in,out] state Pointer to an existing search state structure.
* @param[out] found_device True if a device is found, false if no devices are found.
* If a device is found, the ROM Code can be obtained from the state.
* @return status
*/
owb_status owb_search_first(const OneWireBus * bus, OneWireBus_SearchState * state, bool *found_device);
/**
* @brief Locates the next device on the 1-Wire bus, if present, starting from
* the provided state. Further calls will yield additional devices, if present.
* @param[in] bus Pointer to initialised bus instance.
* @param[in,out] state Pointer to an existing search state structure.
* @param[out] found_device True if a device is found, false if no devices are found.
* If a device is found, the ROM Code can be obtained from the state.
* @return status
*/
owb_status owb_search_next(const OneWireBus * bus, OneWireBus_SearchState * state, bool *found_device);
/**
* @brief Create a string representation of a ROM code, most significant byte (CRC8) first.
* @param[in] rom_code The ROM code to convert to string representation.
* @param[out] buffer The destination for the string representation. It will be null terminated.
* @param[in] len The length of the buffer in bytes. 64-bit ROM codes require 16 characters
* to represent as a string, plus a null terminator, for 17 bytes.
* See OWB_ROM_CODE_STRING_LENGTH.
* @return pointer to the byte beyond the last byte written
*/
char * owb_string_from_rom_code(OneWireBus_ROMCode rom_code, char * buffer, size_t len);
/**
* @brief Enable or disable the strong-pullup GPIO, if configured.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] enable If true, enable the external strong pull-up by setting the GPIO high.
* If false, disable the external strong pull-up by setting the GPIO low.
* @return status
*/
owb_status owb_set_strong_pullup(const OneWireBus * bus, bool enable);
#include "owb_gpio.h"
#include "owb_rmt.h"
#ifdef __cplusplus
}
#endif
#endif // ONE_WIRE_BUS_H

@ -0,0 +1,70 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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
* @brief Interface definitions for the ESP32 GPIO driver used to communicate with devices
* on the One Wire Bus.
*
* @deprecated
* This driver is deprecated and may be removed at some stage. It is not recommended for use
* due to issues with imprecise timing.
*/
#pragma once
#ifndef OWB_GPIO_H
#define OWB_GPIO_H
#include "owb.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief GPIO driver information
*/
typedef struct
{
int gpio; ///< Value of the GPIO connected to the 1-Wire bus
OneWireBus bus; ///< OneWireBus instance
} owb_gpio_driver_info;
/**
* @brief Initialise the GPIO driver.
* @return OneWireBus*, pass this into the other OneWireBus public API functions
*/
OneWireBus * owb_gpio_initialize(owb_gpio_driver_info *driver_info, int gpio);
/**
* @brief Clean up after a call to owb_gpio_initialize()
*/
void owb_gpio_uninitialize(owb_gpio_driver_info *driver_info);
#ifdef __cplusplus
}
#endif
#endif // OWB_GPIO_H

@ -0,0 +1,75 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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
* @brief Interface definitions for ESP32 RMT driver used to communicate with devices
* on the One Wire Bus.
*
* This is the recommended driver.
*/
#pragma once
#ifndef OWB_RMT_H
#define OWB_RMT_H
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
#include "driver/rmt.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief RMT driver information
*/
typedef struct
{
int tx_channel; ///< RMT channel to use for TX
int rx_channel; ///< RMT channel to use for RX
RingbufHandle_t rb; ///< Ring buffer handle
int gpio; ///< OneWireBus GPIO
OneWireBus bus; ///< OneWireBus instance
} owb_rmt_driver_info;
/**
* @brief Initialise the RMT driver.
* @param[in] info Pointer to an uninitialized owb_rmt_driver_info structure.
* Note: the structure must remain in scope for the lifetime of this component.
* @param[in] gpio_num The GPIO number to use as the One Wire bus data line.
* @param[in] tx_channel The RMT channel to use for transmitting data to bus devices.
* @param[in] rx_channel the RMT channel to use for receiving data from bus devices.
* @return OneWireBus *, pass this into the other OneWireBus public API functions
*/
OneWireBus* owb_rmt_initialize(owb_rmt_driver_info * info, gpio_num_t gpio_num,
rmt_channel_t tx_channel, rmt_channel_t rx_channel);
#ifdef __cplusplus
}
#endif
#endif // OWB_RMT_H

@ -0,0 +1,30 @@
{
"name": "esp32-owb",
"keywords": "onewire, 1-wire, bus, sensor, temperature",
"description": "ESP32-compatible C component for the Maxim Integrated 1-Wire protocol.",
"repository":
{
"type": "git",
"url": "https://github.com/DavidAntliff/esp32-owb.git"
},
"authors":
[
{
"name": "David Antliff",
"url": "https://github.com/DavidAntliff",
"maintainer": true
},
{
"name": "Chris Morgan",
"url": "https://github.com/chmorgan"
}
],
"version": "0.1",
"frameworks": "espidf",
"platforms": "espressif32",
"build": {
"flags": [
"-I include/"
]
}
}

@ -0,0 +1,744 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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
*/
#include <stddef.h>
#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "owb.h"
#include "owb_gpio.h"
static const char * TAG = "owb";
static bool _is_init(const OneWireBus * bus)
{
bool ok = false;
if (bus != NULL)
{
if (bus->driver)
{
// OK
ok = true;
}
else
{
ESP_LOGE(TAG, "bus is not initialised");
}
}
else
{
ESP_LOGE(TAG, "bus is NULL");
}
return ok;
}
/**
* @brief 1-Wire 8-bit CRC lookup.
* @param[in] crc Starting CRC value. Pass in prior CRC to accumulate.
* @param[in] data Byte to feed into CRC.
* @return Resultant CRC value.
*/
static uint8_t _calc_crc(uint8_t crc, uint8_t data)
{
// https://www.maximintegrated.com/en/app-notes/index.mvp/id/27
static const uint8_t table[256] = {
0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};
return table[crc ^ data];
}
static uint8_t _calc_crc_block(uint8_t crc, const uint8_t * buffer, size_t len)
{
do
{
crc = _calc_crc(crc, *buffer++);
ESP_LOGD(TAG, "buffer 0x%02x, crc 0x%02x, len %d", (uint8_t)*(buffer - 1), (int)crc, (int)len);
}
while (--len > 0);
return crc;
}
/**
* @param[out] is_found true if a device was found, false if not
* @return status
*/
static owb_status _search(const OneWireBus * bus, OneWireBus_SearchState * state, bool * is_found)
{
// Based on https://www.maximintegrated.com/en/app-notes/index.mvp/id/187
// initialize for search
int id_bit_number = 1;
int last_zero = 0;
int rom_byte_number = 0;
uint8_t id_bit = 0;
uint8_t cmp_id_bit = 0;
uint8_t rom_byte_mask = 1;
uint8_t search_direction = 0;
bool search_result = false;
uint8_t crc8 = 0;
owb_status status = OWB_STATUS_NOT_SET;
// if the last call was not the last one
if (!state->last_device_flag)
{
// 1-Wire reset
bool is_present;
bus->driver->reset(bus, &is_present);
if (!is_present)
{
// reset the search
state->last_discrepancy = 0;
state->last_device_flag = false;
state->last_family_discrepancy = 0;
*is_found = false;
return OWB_STATUS_OK;
}
// issue the search command
bus->driver->write_bits(bus, OWB_ROM_SEARCH, 8);
// loop to do the search
do
{
id_bit = cmp_id_bit = 0;
// read a bit and its complement
bus->driver->read_bits(bus, &id_bit, 1);
bus->driver->read_bits(bus, &cmp_id_bit, 1);
// check for no devices on 1-wire (signal level is high in both bit reads)
if (id_bit && cmp_id_bit)
{
break;
}
else
{
// all devices coupled have 0 or 1
if (id_bit != cmp_id_bit)
{
search_direction = (id_bit) ? 1 : 0; // bit write value for search
}
else
{
// if this discrepancy if before the Last Discrepancy
// on a previous next then pick the same as last time
if (id_bit_number < state->last_discrepancy)
{
search_direction = ((state->rom_code.bytes[rom_byte_number] & rom_byte_mask) > 0);
}
else
{
// if equal to last pick 1, if not then pick 0
search_direction = (id_bit_number == state->last_discrepancy);
}
// if 0 was picked then record its position in LastZero
if (search_direction == 0)
{
last_zero = id_bit_number;
// check for Last discrepancy in family
if (last_zero < 9)
{
state->last_family_discrepancy = last_zero;
}
}
}
// set or clear the bit in the ROM byte rom_byte_number
// with mask rom_byte_mask
if (search_direction == 1)
{
state->rom_code.bytes[rom_byte_number] |= rom_byte_mask;
}
else
{
state->rom_code.bytes[rom_byte_number] &= ~rom_byte_mask;
}
// serial number search direction write bit
bus->driver->write_bits(bus, search_direction, 1);
// increment the byte counter id_bit_number
// and shift the mask rom_byte_mask
id_bit_number++;
rom_byte_mask <<= 1;
// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
if (rom_byte_mask == 0)
{
crc8 = owb_crc8_byte(crc8, state->rom_code.bytes[rom_byte_number]); // accumulate the CRC
rom_byte_number++;
rom_byte_mask = 1;
}
}
}
while (rom_byte_number < 8); // loop until through all ROM bytes 0-7
// if the search was successful then
if (!((id_bit_number < 65) || (crc8 != 0)))
{
// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
state->last_discrepancy = last_zero;
// check for last device
if (state->last_discrepancy == 0)
{
state->last_device_flag = true;
}
search_result = true;
}
}
// if no device found then reset counters so next 'search' will be like a first
if (!search_result || !state->rom_code.bytes[0])
{
state->last_discrepancy = 0;
state->last_device_flag = false;
state->last_family_discrepancy = 0;
search_result = false;
}
status = OWB_STATUS_OK;
*is_found = search_result;
return status;
}
// Public API
owb_status owb_uninitialize(OneWireBus * bus)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bus->driver->uninitialize(bus);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_use_crc(OneWireBus * bus, bool use_crc)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bus->use_crc = use_crc;
ESP_LOGD(TAG, "use_crc %d", bus->use_crc);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_use_parasitic_power(OneWireBus * bus, bool use_parasitic_power)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bus->use_parasitic_power = use_parasitic_power;
ESP_LOGD(TAG, "use_parasitic_power %d", bus->use_parasitic_power);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_use_strong_pullup_gpio(OneWireBus * bus, gpio_num_t gpio)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
if (gpio != GPIO_NUM_NC) {
// The strong GPIO pull-up is only activated if parasitic-power mode is enabled
if (!bus->use_parasitic_power) {
ESP_LOGW(TAG, "Strong pull-up GPIO set with parasitic-power disabled");
}
gpio_pad_select_gpio(gpio);
gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
}
else
{
gpio_reset_pin(bus->strong_pullup_gpio);
}
bus->strong_pullup_gpio = gpio;
ESP_LOGD(TAG, "use_strong_pullup_gpio %d", bus->strong_pullup_gpio);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_read_rom(const OneWireBus * bus, OneWireBus_ROMCode *rom_code)
{
owb_status status = OWB_STATUS_NOT_SET;
memset(rom_code, 0, sizeof(OneWireBus_ROMCode));
if (!bus || !rom_code)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bool is_present;
bus->driver->reset(bus, &is_present);
if (is_present)
{
uint8_t value = OWB_ROM_READ;
bus->driver->write_bits(bus, value, 8);
owb_read_bytes(bus, rom_code->bytes, sizeof(OneWireBus_ROMCode));
if (bus->use_crc)
{
if (owb_crc8_bytes(0, rom_code->bytes, sizeof(OneWireBus_ROMCode)) != 0)
{
ESP_LOGE(TAG, "CRC failed");
memset(rom_code->bytes, 0, sizeof(OneWireBus_ROMCode));
status = OWB_STATUS_CRC_FAILED;
}
else
{
status = OWB_STATUS_OK;
}
}
else
{
status = OWB_STATUS_OK;
}
char rom_code_s[OWB_ROM_CODE_STRING_LENGTH];
owb_string_from_rom_code(*rom_code, rom_code_s, sizeof(rom_code_s));
ESP_LOGD(TAG, "rom_code %s", rom_code_s);
}
else
{
status = OWB_STATUS_DEVICE_NOT_RESPONDING;
ESP_LOGE(TAG, "ds18b20 device not responding");
}
}
return status;
}
owb_status owb_verify_rom(const OneWireBus * bus, OneWireBus_ROMCode rom_code, bool * is_present)
{
owb_status status = OWB_STATUS_NOT_SET;
bool result = false;
if (!bus || !is_present)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
OneWireBus_SearchState state = {
.rom_code = rom_code,
.last_discrepancy = 64,
.last_device_flag = false,
};
bool is_found = false;
_search(bus, &state, &is_found);
if (is_found)
{
result = true;
for (int i = 0; i < sizeof(state.rom_code.bytes) && result; ++i)
{
result = rom_code.bytes[i] == state.rom_code.bytes[i];
ESP_LOGD(TAG, "%02x %02x", rom_code.bytes[i], state.rom_code.bytes[i]);
}
is_found = result;
}
ESP_LOGD(TAG, "state.last_discrepancy %d, state.last_device_flag %d, is_found %d",
state.last_discrepancy, state.last_device_flag, is_found);
ESP_LOGD(TAG, "rom code %sfound", result ? "" : "not ");
*is_present = result;
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_reset(const OneWireBus * bus, bool * a_device_present)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !a_device_present)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if(!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
status = bus->driver->reset(bus, a_device_present);
}
return status;
}
owb_status owb_read_bit(const OneWireBus * bus, uint8_t * out)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !out)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bus->driver->read_bits(bus, out, 1);
ESP_LOGD(TAG, "owb_read_bit: %02x", *out);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_read_byte(const OneWireBus * bus, uint8_t * out)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !out)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bus->driver->read_bits(bus, out, 8);
ESP_LOGD(TAG, "owb_read_byte: %02x", *out);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_read_bytes(const OneWireBus * bus, uint8_t * buffer, unsigned int len)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !buffer)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
for (int i = 0; i < len; ++i)
{
uint8_t out;
bus->driver->read_bits(bus, &out, 8);
buffer[i] = out;
}
ESP_LOGD(TAG, "owb_read_bytes, len %d:", len);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, len, ESP_LOG_DEBUG);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_bit(const OneWireBus * bus, const uint8_t bit)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
ESP_LOGD(TAG, "owb_write_bit: %02x", bit);
bus->driver->write_bits(bus, bit & 0x01u, 1);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_byte(const OneWireBus * bus, uint8_t data)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
ESP_LOGD(TAG, "owb_write_byte: %02x", data);
bus->driver->write_bits(bus, data, 8);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_bytes(const OneWireBus * bus, const uint8_t * buffer, size_t len)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !buffer)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
ESP_LOGD(TAG, "owb_write_bytes, len %d:", len);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, len, ESP_LOG_DEBUG);
for (int i = 0; i < len; i++)
{
bus->driver->write_bits(bus, buffer[i], 8);
}
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_rom_code(const OneWireBus * bus, OneWireBus_ROMCode rom_code)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
owb_write_bytes(bus, (uint8_t*)&rom_code, sizeof(rom_code));
status = OWB_STATUS_OK;
}
return status;
}
uint8_t owb_crc8_byte(uint8_t crc, uint8_t data)
{
return _calc_crc(crc, data);
}
uint8_t owb_crc8_bytes(uint8_t crc, const uint8_t * data, size_t len)
{
return _calc_crc_block(crc, data, len);
}
owb_status owb_search_first(const OneWireBus * bus, OneWireBus_SearchState * state, bool * found_device)
{
bool result;
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !state || !found_device)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
memset(&state->rom_code, 0, sizeof(state->rom_code));
state->last_discrepancy = 0;
state->last_family_discrepancy = 0;
state->last_device_flag = false;
_search(bus, state, &result);
status = OWB_STATUS_OK;
*found_device = result;
}
return status;
}
owb_status owb_search_next(const OneWireBus * bus, OneWireBus_SearchState * state, bool * found_device)
{
owb_status status = OWB_STATUS_NOT_SET;
bool result = false;
if (!bus || !state || !found_device)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
_search(bus, state, &result);
status = OWB_STATUS_OK;
*found_device = result;
}
return status;
}
char * owb_string_from_rom_code(OneWireBus_ROMCode rom_code, char * buffer, size_t len)
{
for (int i = sizeof(rom_code.bytes) - 1; i >= 0; i--)
{
sprintf(buffer, "%02x", rom_code.bytes[i]);
buffer += 2;
}
return buffer;
}
owb_status owb_set_strong_pullup(const OneWireBus * bus, bool enable)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
if (bus->use_parasitic_power && bus->strong_pullup_gpio != GPIO_NUM_NC)
{
gpio_set_level(bus->strong_pullup_gpio, enable ? 1 : 0);
ESP_LOGD(TAG, "strong pullup GPIO %d", enable);
} // else ignore
status = OWB_STATUS_OK;
}
return status;
}

@ -0,0 +1,287 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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
*/
#include <stddef.h>
#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "owb.h"
#include "owb_gpio.h"
static const char * TAG = "owb_gpio";
// Define PHY_DEBUG to enable GPIO output around when the bus is sampled
// by the master (this library). This GPIO output makes it possible to
// validate the master's sampling using an oscilloscope.
//
// For the debug GPIO the idle state is low and made high before the 1-wire sample
// point and low again after the sample point
#undef PHY_DEBUG
#ifdef PHY_DEBUG
// Update these defines to a pin that you can access
#define PHY_DEBUG_GPIO GPIO_NUM_27
#define PHY_DEBUG_GPIO_MASK GPIO_SEL_27
#endif
/// @cond ignore
struct _OneWireBus_Timing
{
uint32_t A, B, C, D, E, F, G, H, I, J;
};
/// @endcond
// 1-Wire timing delays (standard) in microseconds.
// Labels and values are from https://www.maximintegrated.com/en/app-notes/index.mvp/id/126
static const struct _OneWireBus_Timing _StandardTiming = {
6, // A - read/write "1" master pull DQ low duration
64, // B - write "0" master pull DQ low duration
60, // C - write "1" master pull DQ high duration
10, // D - write "0" master pull DQ high duration
9, // E - read master pull DQ high duration
55, // F - complete read timeslot + 10ms recovery
0, // G - wait before reset
480, // H - master pull DQ low duration
70, // I - master pull DQ high duration
410, // J - complete presence timeslot + recovery
};
static void _us_delay(uint32_t time_us)
{
ets_delay_us(time_us);
}
/// @cond ignore
#define info_from_bus(owb) container_of(owb, owb_gpio_driver_info, bus)
/// @endcond
/**
* @brief Generate a 1-Wire reset (initialization).
* @param[in] bus Initialised bus instance.
* @param[out] is_present true if device is present, otherwise false.
* @return status
*/
static owb_status _reset(const OneWireBus * bus, bool * is_present)
{
bool present = false;
portMUX_TYPE timeCriticalMutex = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&timeCriticalMutex);
owb_gpio_driver_info *i = info_from_bus(bus);
gpio_set_direction(i->gpio, GPIO_MODE_OUTPUT);
_us_delay(bus->timing->G);
gpio_set_level(i->gpio, 0); // Drive DQ low
_us_delay(bus->timing->H);
gpio_set_direction(i->gpio, GPIO_MODE_INPUT); // Release the bus
gpio_set_level(i->gpio, 1); // Reset the output level for the next output
_us_delay(bus->timing->I);
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 1);
#endif
int level1 = gpio_get_level(i->gpio);
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 0);
#endif
_us_delay(bus->timing->J); // Complete the reset sequence recovery
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 1);
#endif
int level2 = gpio_get_level(i->gpio);
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 0);
#endif
portEXIT_CRITICAL(&timeCriticalMutex);
present = (level1 == 0) && (level2 == 1); // Sample for presence pulse from slave
ESP_LOGD(TAG, "reset: level1 0x%x, level2 0x%x, present %d", level1, level2, present);
*is_present = present;
return OWB_STATUS_OK;
}
/**
* @brief Send a 1-Wire write bit, with recovery time.
* @param[in] bus Initialised bus instance.
* @param[in] bit The value to send.
*/
static void _write_bit(const OneWireBus * bus, int bit)
{
int delay1 = bit ? bus->timing->A : bus->timing->C;
int delay2 = bit ? bus->timing->B : bus->timing->D;
owb_gpio_driver_info *i = info_from_bus(bus);
portMUX_TYPE timeCriticalMutex = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&timeCriticalMutex);
gpio_set_direction(i->gpio, GPIO_MODE_OUTPUT);
gpio_set_level(i->gpio, 0); // Drive DQ low
_us_delay(delay1);
gpio_set_level(i->gpio, 1); // Release the bus
_us_delay(delay2);
portEXIT_CRITICAL(&timeCriticalMutex);
}
/**
* @brief Read a bit from the 1-Wire bus and return the value, with recovery time.
* @param[in] bus Initialised bus instance.
*/
static int _read_bit(const OneWireBus * bus)
{
int result = 0;
owb_gpio_driver_info *i = info_from_bus(bus);
portMUX_TYPE timeCriticalMutex = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&timeCriticalMutex);
gpio_set_direction(i->gpio, GPIO_MODE_OUTPUT);
gpio_set_level(i->gpio, 0); // Drive DQ low
_us_delay(bus->timing->A);
gpio_set_direction(i->gpio, GPIO_MODE_INPUT); // Release the bus
gpio_set_level(i->gpio, 1); // Reset the output level for the next output
_us_delay(bus->timing->E);
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 1);
#endif
int level = gpio_get_level(i->gpio);
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 0);
#endif
_us_delay(bus->timing->F); // Complete the timeslot and 10us recovery
portEXIT_CRITICAL(&timeCriticalMutex);
result = level & 0x01;
return result;
}
/**
* @brief Write 1-Wire data byte.
* NOTE: The data is shifted out of the low bits, eg. it is written in the order of lsb to msb
* @param[in] bus Initialised bus instance.
* @param[in] data Value to write.
* @param[in] number_of_bits_to_read bits to write
*/
static owb_status _write_bits(const OneWireBus * bus, uint8_t data, int number_of_bits_to_write)
{
ESP_LOGD(TAG, "write 0x%02x", data);
for (int i = 0; i < number_of_bits_to_write; ++i)
{
_write_bit(bus, data & 0x01);
data >>= 1;
}
return OWB_STATUS_OK;
}
/**
* @brief Read 1-Wire data byte from bus.
* NOTE: Data is read into the high bits, eg. each bit read is shifted down before the next bit is read
* @param[in] bus Initialised bus instance.
* @return Byte value read from bus.
*/
static owb_status _read_bits(const OneWireBus * bus, uint8_t *out, int number_of_bits_to_read)
{
uint8_t result = 0;
for (int i = 0; i < number_of_bits_to_read; ++i)
{
result >>= 1;
if (_read_bit(bus))
{
result |= 0x80;
}
}
ESP_LOGD(TAG, "read 0x%02x", result);
*out = result;
return OWB_STATUS_OK;
}
static owb_status _uninitialize(const OneWireBus * bus)
{
// Nothing to do here for this driver_info
return OWB_STATUS_OK;
}
static const struct owb_driver gpio_function_table =
{
.name = "owb_gpio",
.uninitialize = _uninitialize,
.reset = _reset,
.write_bits = _write_bits,
.read_bits = _read_bits
};
OneWireBus* owb_gpio_initialize(owb_gpio_driver_info * driver_info, int gpio)
{
ESP_LOGD(TAG, "%s(): gpio %d\n", __func__, gpio);
driver_info->gpio = gpio;
driver_info->bus.driver = &gpio_function_table;
driver_info->bus.timing = &_StandardTiming;
driver_info->bus.strong_pullup_gpio = GPIO_NUM_NC;
// platform specific:
gpio_pad_select_gpio(driver_info->gpio);
#ifdef PHY_DEBUG
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = PHY_DEBUG_GPIO_MASK;
io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
ESP_ERROR_CHECK(gpio_config(&io_conf));
#endif
return &(driver_info->bus);
}

@ -0,0 +1,469 @@
/*
Created by Chris Morgan based on the nodemcu project driver.
Copyright 2017 Chris Morgan <chmorgan@gmail.com>
Ported to ESP32 RMT peripheral for low-level signal generation by Arnim Laeuger.
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.
Much of the code was inspired by Derek Yerger's code, though I don't
think much of that remains. In any event that was..
(copyleft) 2006 by Derek Yerger - Free to distribute freely.
The CRC code was excerpted and inspired by the Dallas Semiconductor
sample code bearing this copyright.
//---------------------------------------------------------------------------
// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
//
// 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 DALLAS SEMICONDUCTOR 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.
//
// Except as contained in this notice, the name of Dallas Semiconductor
// shall not be used except as stated in the Dallas Semiconductor
// Branding Policy.
//--------------------------------------------------------------------------
*/
#include "owb.h"
#include "driver/rmt.h"
#include "driver/gpio.h"
#include "esp_log.h"
#undef OW_DEBUG
// bus reset: duration of low phase [us]
#define OW_DURATION_RESET 480
// overall slot duration
#define OW_DURATION_SLOT 75
// write 1 slot and read slot durations [us]
#define OW_DURATION_1_LOW 2
#define OW_DURATION_1_HIGH (OW_DURATION_SLOT - OW_DURATION_1_LOW)
// write 0 slot durations [us]
#define OW_DURATION_0_LOW 65
#define OW_DURATION_0_HIGH (OW_DURATION_SLOT - OW_DURATION_0_LOW)
// sample time for read slot
#define OW_DURATION_SAMPLE (15-2)
// RX idle threshold
// needs to be larger than any duration occurring during write slots
#define OW_DURATION_RX_IDLE (OW_DURATION_SLOT + 2)
// maximum number of bits that can be read or written per slot
#define MAX_BITS_PER_SLOT (8)
static const char * TAG = "owb_rmt";
#define info_of_driver(owb) container_of(owb, owb_rmt_driver_info, bus)
// flush any pending/spurious traces from the RX channel
static void onewire_flush_rmt_rx_buf(const OneWireBus * bus)
{
void * p = NULL;
size_t s = 0;
owb_rmt_driver_info * i = info_of_driver(bus);
while ((p = xRingbufferReceive(i->rb, &s, 0)))
{
ESP_LOGD(TAG, "flushing entry");
vRingbufferReturnItem(i->rb, p);
}
}
static owb_status _reset(const OneWireBus * bus, bool * is_present)
{
rmt_item32_t tx_items[1] = {0};
bool _is_present = false;
int res = OWB_STATUS_OK;
owb_rmt_driver_info * i = info_of_driver(bus);
tx_items[0].duration0 = OW_DURATION_RESET;
tx_items[0].level0 = 0;
tx_items[0].duration1 = 0;
tx_items[0].level1 = 1;
uint16_t old_rx_thresh = 0;
rmt_get_rx_idle_thresh(i->rx_channel, &old_rx_thresh);
rmt_set_rx_idle_thresh(i->rx_channel, OW_DURATION_RESET + 60);
onewire_flush_rmt_rx_buf(bus);
rmt_rx_start(i->rx_channel, true);
if (rmt_write_items(i->tx_channel, tx_items, 1, true) == ESP_OK)
{
size_t rx_size = 0;
rmt_item32_t * rx_items = (rmt_item32_t *)xRingbufferReceive(i->rb, &rx_size, 100 / portTICK_PERIOD_MS);
if (rx_items)
{
if (rx_size >= (1 * sizeof(rmt_item32_t)))
{
#ifdef OW_DEBUG
ESP_LOGI(TAG, "rx_size: %d", rx_size);
for (int i = 0; i < (rx_size / sizeof(rmt_item32_t)); i++)
{
ESP_LOGI(TAG, "i: %d, level0: %d, duration %d", i, rx_items[i].level0, rx_items[i].duration0);
ESP_LOGI(TAG, "i: %d, level1: %d, duration %d", i, rx_items[i].level1, rx_items[i].duration1);
}
#endif
// parse signal and search for presence pulse
if ((rx_items[0].level0 == 0) && (rx_items[0].duration0 >= OW_DURATION_RESET - 2))
{
if ((rx_items[0].level1 == 1) && (rx_items[0].duration1 > 0))
{
if (rx_items[1].level0 == 0)
{
_is_present = true;
}
}
}
}
vRingbufferReturnItem(i->rb, (void *)rx_items);
}
else
{
// time out occurred, this indicates an unconnected / misconfigured bus
ESP_LOGE(TAG, "rx_items == 0");
res = OWB_STATUS_HW_ERROR;
}
}
else
{
// error in tx channel
ESP_LOGE(TAG, "Error tx");
res = OWB_STATUS_HW_ERROR;
}
rmt_rx_stop(i->rx_channel);
rmt_set_rx_idle_thresh(i->rx_channel, old_rx_thresh);
*is_present = _is_present;
ESP_LOGD(TAG, "_is_present %d", _is_present);
return res;
}
static rmt_item32_t _encode_write_slot(uint8_t val)
{
rmt_item32_t item = {0};
item.level0 = 0;
item.level1 = 1;
if (val)
{
// write "1" slot
item.duration0 = OW_DURATION_1_LOW;
item.duration1 = OW_DURATION_1_HIGH;
}
else
{
// write "0" slot
item.duration0 = OW_DURATION_0_LOW;
item.duration1 = OW_DURATION_0_HIGH;
}
return item;
}
/** NOTE: The data is shifted out of the low bits, eg. it is written in the order of lsb to msb */
static owb_status _write_bits(const OneWireBus * bus, uint8_t out, int number_of_bits_to_write)
{
rmt_item32_t tx_items[MAX_BITS_PER_SLOT + 1] = {0};
owb_rmt_driver_info * info = info_of_driver(bus);
if (number_of_bits_to_write > MAX_BITS_PER_SLOT)
{
return OWB_STATUS_TOO_MANY_BITS;
}
// write requested bits as pattern to TX buffer
for (int i = 0; i < number_of_bits_to_write; i++)
{
tx_items[i] = _encode_write_slot(out & 0x01);
out >>= 1;
}
// end marker
tx_items[number_of_bits_to_write].level0 = 1;
tx_items[number_of_bits_to_write].duration0 = 0;
owb_status status = OWB_STATUS_NOT_SET;
if (rmt_write_items(info->tx_channel, tx_items, number_of_bits_to_write+1, true) == ESP_OK)
{
status = OWB_STATUS_OK;
}
else
{
status = OWB_STATUS_HW_ERROR;
ESP_LOGE(TAG, "rmt_write_items() failed");
}
return status;
}
static rmt_item32_t _encode_read_slot(void)
{
rmt_item32_t item = {0};
// construct pattern for a single read time slot
item.level0 = 0;
item.duration0 = OW_DURATION_1_LOW; // shortly force 0
item.level1 = 1;
item.duration1 = OW_DURATION_1_HIGH; // release high and finish slot
return item;
}
/** NOTE: Data is read into the high bits, eg. each bit read is shifted down before the next bit is read */
static owb_status _read_bits(const OneWireBus * bus, uint8_t *in, int number_of_bits_to_read)
{
rmt_item32_t tx_items[MAX_BITS_PER_SLOT + 1] = {0};
uint8_t read_data = 0;
int res = OWB_STATUS_OK;
owb_rmt_driver_info *info = info_of_driver(bus);
if (number_of_bits_to_read > MAX_BITS_PER_SLOT)
{
ESP_LOGE(TAG, "_read_bits() OWB_STATUS_TOO_MANY_BITS");
return OWB_STATUS_TOO_MANY_BITS;
}
// generate requested read slots
for (int i = 0; i < number_of_bits_to_read; i++)
{
tx_items[i] = _encode_read_slot();
}
// end marker
tx_items[number_of_bits_to_read].level0 = 1;
tx_items[number_of_bits_to_read].duration0 = 0;
onewire_flush_rmt_rx_buf(bus);
rmt_rx_start(info->rx_channel, true);
if (rmt_write_items(info->tx_channel, tx_items, number_of_bits_to_read+1, true) == ESP_OK)
{
size_t rx_size = 0;
rmt_item32_t *rx_items = (rmt_item32_t *)xRingbufferReceive(info->rb, &rx_size, 100 / portTICK_PERIOD_MS);
if (rx_items)
{
#ifdef OW_DEBUG
for (int i = 0; i < rx_size / 4; i++)
{
ESP_LOGI(TAG, "level: %d, duration %d", rx_items[i].level0, rx_items[i].duration0);
ESP_LOGI(TAG, "level: %d, duration %d", rx_items[i].level1, rx_items[i].duration1);
}
#endif
if (rx_size >= number_of_bits_to_read * sizeof(rmt_item32_t))
{
for (int i = 0; i < number_of_bits_to_read; i++)
{
read_data >>= 1;
// parse signal and identify logical bit
if (rx_items[i].level1 == 1)
{
if ((rx_items[i].level0 == 0) && (rx_items[i].duration0 < OW_DURATION_SAMPLE))
{
// rising edge occured before 15us -> bit 1
read_data |= 0x80;
}
}
}
read_data >>= 8 - number_of_bits_to_read;
}
vRingbufferReturnItem(info->rb, (void *)rx_items);
}
else
{
// time out occurred, this indicates an unconnected / misconfigured bus
ESP_LOGE(TAG, "rx_items == 0");
res = OWB_STATUS_HW_ERROR;
}
}
else
{
// error in tx channel
ESP_LOGE(TAG, "Error tx");
res = OWB_STATUS_HW_ERROR;
}
rmt_rx_stop(info->rx_channel);
*in = read_data;
return res;
}
static owb_status _uninitialize(const OneWireBus *bus)
{
owb_rmt_driver_info * info = info_of_driver(bus);
rmt_driver_uninstall(info->tx_channel);
rmt_driver_uninstall(info->rx_channel);
return OWB_STATUS_OK;
}
static struct owb_driver rmt_function_table =
{
.name = "owb_rmt",
.uninitialize = _uninitialize,
.reset = _reset,
.write_bits = _write_bits,
.read_bits = _read_bits
};
static owb_status _init(owb_rmt_driver_info *info, gpio_num_t gpio_num,
rmt_channel_t tx_channel, rmt_channel_t rx_channel)
{
owb_status status = OWB_STATUS_HW_ERROR;
// Ensure the RMT peripheral is not already running
// Note: if using RMT elsewhere, don't call this here, call it at the start of your program instead.
//periph_module_disable(PERIPH_RMT_MODULE);
//periph_module_enable(PERIPH_RMT_MODULE);
info->bus.driver = &rmt_function_table;
info->tx_channel = tx_channel;
info->rx_channel = rx_channel;
info->gpio = gpio_num;
#ifdef OW_DEBUG
ESP_LOGI(TAG, "RMT TX channel: %d", info->tx_channel);
ESP_LOGI(TAG, "RMT RX channel: %d", info->rx_channel);
#endif
rmt_config_t rmt_tx = {0};
rmt_tx.channel = info->tx_channel;
rmt_tx.gpio_num = gpio_num;
rmt_tx.mem_block_num = 1;
rmt_tx.clk_div = 80;
rmt_tx.tx_config.loop_en = false;
rmt_tx.tx_config.carrier_en = false;
rmt_tx.tx_config.idle_level = 1;
rmt_tx.tx_config.idle_output_en = true;
rmt_tx.rmt_mode = RMT_MODE_TX;
if (rmt_config(&rmt_tx) == ESP_OK)
{
rmt_set_source_clk(info->tx_channel, RMT_BASECLK_APB); // only APB is supported by IDF 4.2
if (rmt_driver_install(rmt_tx.channel, 0, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED) == ESP_OK)
{
rmt_config_t rmt_rx = {0};
rmt_rx.channel = info->rx_channel;
rmt_rx.gpio_num = gpio_num;
rmt_rx.clk_div = 80;
rmt_rx.mem_block_num = 1;
rmt_rx.rmt_mode = RMT_MODE_RX;
rmt_rx.rx_config.filter_en = true;
rmt_rx.rx_config.filter_ticks_thresh = 30;
rmt_rx.rx_config.idle_threshold = OW_DURATION_RX_IDLE;
if (rmt_config(&rmt_rx) == ESP_OK)
{
rmt_set_source_clk(info->rx_channel, RMT_BASECLK_APB); // only APB is supported by IDF 4.2
if (rmt_driver_install(rmt_rx.channel, 512, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED) == ESP_OK)
{
rmt_get_ringbuf_handle(info->rx_channel, &info->rb);
status = OWB_STATUS_OK;
}
else
{
ESP_LOGE(TAG, "failed to install rx driver");
}
}
else
{
status = OWB_STATUS_HW_ERROR;
ESP_LOGE(TAG, "failed to configure rx, uninstalling rmt driver on tx channel");
rmt_driver_uninstall(rmt_tx.channel);
}
}
else
{
ESP_LOGE(TAG, "failed to install tx driver");
}
}
else
{
ESP_LOGE(TAG, "failed to configure tx");
}
// attach GPIO to previous pin
if (gpio_num < 32)
{
GPIO.enable_w1ts = (0x1 << gpio_num);
}
else
{
GPIO.enable1_w1ts.data = (0x1 << (gpio_num - 32));
}
// attach RMT channels to new gpio pin
// ATTENTION: set pin for rx first since gpio_output_disable() will
// remove rmt output signal in matrix!
rmt_set_gpio(info->rx_channel, RMT_MODE_RX, gpio_num, false);
rmt_set_gpio(info->tx_channel, RMT_MODE_TX, gpio_num, false);
// force pin direction to input to enable path to RX channel
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[gpio_num]);
// enable open drain
GPIO.pin[gpio_num].pad_driver = 1;
return status;
}
OneWireBus * owb_rmt_initialize(owb_rmt_driver_info * info, gpio_num_t gpio_num,
rmt_channel_t tx_channel, rmt_channel_t rx_channel)
{
ESP_LOGD(TAG, "%s: gpio_num: %d, tx_channel: %d, rx_channel: %d",
__func__, gpio_num, tx_channel, rx_channel);
owb_status status = _init(info, gpio_num, tx_channel, rx_channel);
if (status != OWB_STATUS_OK)
{
ESP_LOGE(TAG, "_init() failed with status %d", status);
}
info->bus.strong_pullup_gpio = GPIO_NUM_NC;
return &(info->bus);
}
Loading…
Cancel
Save