This commit is contained in:
Alexey Zholtikov 2024-05-20 18:01:46 +03:00
parent 8b8ef15dd9
commit 394a9cbd2b
35 changed files with 2058 additions and 15 deletions

View File

@ -42,7 +42,7 @@
"esp_heap_caps.h": "c",
"zh_vector.h": "c"
},
"idf.adapterTargetName": "esp32",
"idf.adapterTargetName": "esp8266",
"idf.port": "/dev/cu.usbserial-0001",
"idf.flashType": "UART",
"idf.openOcdConfigs": [

BIN
components/espressif_ds18b20/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,7 @@
## 0.1.1
- Fix the issue that sign-bit is not extended properly when doing temperature value conversion.
## 0.1.0
- Initial driver version, based on the [onewire_bus](https://components.espressif.com/components/espressif/onewire_bus) library.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "src/ds18b20.c"
INCLUDE_DIRS "include")

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,63 @@
# DS18B20 Device Driver
[![Component Registry](https://components.espressif.com/components/espressif/ds18b20/badge.svg)](https://components.espressif.com/components/espressif/ds18b20)
DS18B20 temperature sensor only uses a single wire to write and read data, the interface is also called the `1-Wire` bus. This component only contains the sensor driver. For 1-Wire bus setup, you need to use the [onewire_bus](https://components.espressif.com/components/espressif/onewire_bus) library to initialize and enumerate the devices.
## How to enumerate DS18B20 devices on the 1-Wire bus
```c
#define EXAMPLE_ONEWIRE_BUS_GPIO 0
#define EXAMPLE_ONEWIRE_MAX_DS18B20 2
// install 1-wire bus
onewire_bus_handle_t bus = NULL;
onewire_bus_config_t bus_config = {
.bus_gpio_num = EXAMPLE_ONEWIRE_BUS_GPIO,
};
onewire_bus_rmt_config_t rmt_config = {
.max_rx_bytes = 10, // 1byte ROM command + 8byte ROM number + 1byte device command
};
ESP_ERROR_CHECK(onewire_new_bus_rmt(&bus_config, &rmt_config, &bus));
int ds18b20_device_num = 0;
ds18b20_device_handle_t ds18b20s[EXAMPLE_ONEWIRE_MAX_DS18B20];
onewire_device_iter_handle_t iter = NULL;
onewire_device_t next_onewire_device;
esp_err_t search_result = ESP_OK;
// create 1-wire device iterator, which is used for device search
ESP_ERROR_CHECK(onewire_new_device_iter(bus, &iter));
ESP_LOGI(TAG, "Device iterator created, start searching...");
do {
search_result = onewire_device_iter_get_next(iter, &next_onewire_device);
if (search_result == ESP_OK) { // found a new device, let's check if we can upgrade it to a DS18B20
ds18b20_config_t ds_cfg = {};
// check if the device is a DS18B20, if so, return the ds18b20 handle
if (ds18b20_new_device(&next_onewire_device, &ds_cfg, &ds18b20s[ds18b20_device_num]) == ESP_OK) {
ESP_LOGI(TAG, "Found a DS18B20[%d], address: %016llX", ds18b20_device_num, next_onewire_device.address);
ds18b20_device_num++;
} else {
ESP_LOGI(TAG, "Found an unknown device, address: %016llX", next_onewire_device.address);
}
}
} while (search_result != ESP_ERR_NOT_FOUND);
ESP_ERROR_CHECK(onewire_del_device_iter(iter));
ESP_LOGI(TAG, "Searching done, %d DS18B20 device(s) found", ds18b20_device_num);
// Now you have the DS18B20 sensor handle, you can use it to read the temperature
```
## Trigger a temperature conversion and then read the data
```c
for (int i = 0; i < ds18b20_device_num; i ++) {
ESP_ERROR_CHECK(ds18b20_trigger_temperature_conversion(ds18b20s[i]));
ESP_ERROR_CHECK(ds18b20_get_temperature(ds18b20s[i], &temperature));
ESP_LOGI(TAG, "temperature read from DS18B20[%d]: %.2fC", i, temperature);
}
```
## Reference
* See [DS18B20 datasheet](https://www.analog.com/media/en/technical-documentation/data-sheets/ds18b20.pdf)

View File

@ -0,0 +1,6 @@
dependencies:
onewire_bus:
version: ^1.0.0
description: DS18B20 device driver
url: https://github.com/espressif/esp-bsp/tree/master/components/ds18b20
version: 0.1.1

View File

@ -0,0 +1,97 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "onewire_device.h"
#include "ds18b20_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Type of DS18B20 device handle
*/
typedef struct ds18b20_device_t *ds18b20_device_handle_t;
/**
* @brief DS18B20 configuration
*/
typedef struct {
} ds18b20_config_t;
/**
* @brief Create a new DS18B20 device based on the general 1-Wire device
*
* @note The general 1-Wire device can be enumerated during the 1-Wire bus device search process,
* this function is going to check and upgrade that into a DS18B20 device.
*
* @param[in] device 1-Wire device basic information, including bus handle and device ROM ID
* @param[in] config DS18B20 configuration
* @param[out] ret_ds18b20 Returned DS18B20 device handle
* @return
* - ESP_OK: Create DS18B20 device successfully
* - ESP_ERR_INVALID_ARG: Create DS18B20 device failed due to invalid argument
* - ESP_ERR_NO_MEM: Create DS18B20 device failed due to out of memory
* - ESP_ERR_NOT_SUPPORTED: Create DS18B20 device failed because the device is unknown (e.g. a wrong family ID code)
* - ESP_FAIL: Create DS18B20 device failed due to other reasons
*/
esp_err_t ds18b20_new_device(onewire_device_t *device, const ds18b20_config_t *config, ds18b20_device_handle_t *ret_ds18b20);
/**
* @brief Delete DS18B20 device
*
* @param ds18b20 DS18B20 device handle returned by `ds18b20_new_device`
* @return
* - ESP_OK: Delete DS18B20 device successfully
* - ESP_ERR_INVALID_ARG: Delete DS18B20 device failed due to invalid argument
* - ESP_FAIL: Delete DS18B20 device failed due to other reasons
*/
esp_err_t ds18b20_del_device(ds18b20_device_handle_t ds18b20);
/**
* @brief Set DS18B20's temperature conversion resolution
*
* @param[in] ds18b20 DS18B20 device handle returned by `ds18b20_new_device`
* @param[in] resolution resolution of DS18B20's temperature conversion
* @return
* - ESP_OK: Set resolution successfully
* - ESP_ERR_INVALID_ARG: Set resolution failed due to invalid argument
* - ESP_FAIL: Set resolution failed due to other reasons
*/
esp_err_t ds18b20_set_resolution(ds18b20_device_handle_t ds18b20, ds18b20_resolution_t resolution);
/**
* @brief Trigger temperature conversion of DS18B20
*
* @note After send the trigger command, the DS18B20 will start temperature conversion.
* This function will delay for some while, to ensure the temperature conversion won't be interrupted.
*
* @param[in] ds18b20 DS18B20 device handle returned by `ds18b20_new_device`
* @return
* - ESP_OK: Trigger temperature conversion successfully
* - ESP_ERR_INVALID_ARG: Trigger temperature conversion failed due to invalid argument
* - ESP_FAIL: Trigger temperature conversion failed due to other reasons
*/
esp_err_t ds18b20_trigger_temperature_conversion(ds18b20_device_handle_t ds18b20);
/**
* @brief Get temperature from DS18B20
*
* @param[in] ds18b20 DS18B20 device handle returned by `ds18b20_new_device`
* @param[out] temperature conversion result from DS18B20
* @return
* - ESP_OK: Get temperature successfully
* - ESP_ERR_INVALID_ARG: Get temperature failed due to invalid argument
* - ESP_ERR_INVALID_CRC: Get temperature failed due to CRC check error
* - ESP_FAIL: Get temperature failed due to other reasons
*/
esp_err_t ds18b20_get_temperature(ds18b20_device_handle_t ds18b20, float *temperature);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief DS18B20 supported resolutions
*/
typedef enum {
DS18B20_RESOLUTION_9B, /*!< 9bit, needs ~93.75ms convert time */
DS18B20_RESOLUTION_10B, /*!< 10bit, needs ~187.5ms convert time */
DS18B20_RESOLUTION_11B, /*!< 11bit, needs ~375ms convert time */
DS18B20_RESOLUTION_12B, /*!< 12bit, needs ~750ms convert time */
} ds18b20_resolution_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,143 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_check.h"
#include "onewire_bus.h"
#include "onewire_cmd.h"
#include "onewire_crc.h"
#include "ds18b20.h"
static const char *TAG = "ds18b20";
#define DS18B20_CMD_CONVERT_TEMP 0x44
#define DS18B20_CMD_WRITE_SCRATCHPAD 0x4E
#define DS18B20_CMD_READ_SCRATCHPAD 0xBE
/**
* @brief Structure of DS18B20's scratchpad
*/
typedef struct {
uint8_t temp_lsb; /*!< lsb of temperature */
uint8_t temp_msb; /*!< msb of temperature */
uint8_t th_user1; /*!< th register or user byte 1 */
uint8_t tl_user2; /*!< tl register or user byte 2 */
uint8_t configuration; /*!< resolution configuration register */
uint8_t _reserved1;
uint8_t _reserved2;
uint8_t _reserved3;
uint8_t crc_value; /*!< crc value of scratchpad data */
} __attribute__((packed)) ds18b20_scratchpad_t;
typedef struct ds18b20_device_t {
onewire_bus_handle_t bus;
onewire_device_address_t addr;
uint8_t th_user1;
uint8_t tl_user2;
ds18b20_resolution_t resolution;
} ds18b20_device_t;
esp_err_t ds18b20_new_device(onewire_device_t *device, const ds18b20_config_t *config, ds18b20_device_handle_t *ret_ds18b20)
{
ds18b20_device_t *ds18b20 = NULL;
ESP_RETURN_ON_FALSE(device && config && ret_ds18b20, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// check ROM ID, the family code of DS18B20 is 0x28
if ((device->address & 0xFF) != 0x28) {
ESP_LOGD(TAG, "%016llX is not a DS18B20 device", device->address);
return ESP_ERR_NOT_SUPPORTED;
}
ds18b20 = calloc(1, sizeof(ds18b20_device_t));
ESP_RETURN_ON_FALSE(ds18b20, ESP_ERR_NO_MEM, TAG, "no mem for ds18b20");
ds18b20->bus = device->bus;
ds18b20->addr = device->address;
ds18b20->resolution = DS18B20_RESOLUTION_12B; // DS18B20 default resolution is 12 bits
*ret_ds18b20 = ds18b20;
return ESP_OK;
}
esp_err_t ds18b20_del_device(ds18b20_device_handle_t ds18b20)
{
ESP_RETURN_ON_FALSE(ds18b20, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
free(ds18b20);
return ESP_OK;
}
static esp_err_t ds18b20_send_command(ds18b20_device_handle_t ds18b20, uint8_t cmd)
{
// send command
uint8_t tx_buffer[10] = {0};
tx_buffer[0] = ONEWIRE_CMD_MATCH_ROM;
memcpy(&tx_buffer[1], &ds18b20->addr, sizeof(ds18b20->addr));
tx_buffer[sizeof(ds18b20->addr) + 1] = cmd;
return onewire_bus_write_bytes(ds18b20->bus, tx_buffer, sizeof(tx_buffer));
}
esp_err_t ds18b20_set_resolution(ds18b20_device_handle_t ds18b20, ds18b20_resolution_t resolution)
{
ESP_RETURN_ON_FALSE(ds18b20, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// reset bus and check if the ds18b20 is present
ESP_RETURN_ON_ERROR(onewire_bus_reset(ds18b20->bus), TAG, "reset bus error");
// send command: DS18B20_CMD_WRITE_SCRATCHPAD
ESP_RETURN_ON_ERROR(ds18b20_send_command(ds18b20, DS18B20_CMD_WRITE_SCRATCHPAD), TAG, "send DS18B20_CMD_WRITE_SCRATCHPAD failed");
// write new resolution to scratchpad
const uint8_t resolution_data[] = {0x1F, 0x3F, 0x5F, 0x7F};
uint8_t tx_buffer[3] = {0};
tx_buffer[0] = ds18b20->th_user1;
tx_buffer[1] = ds18b20->tl_user2;
tx_buffer[2] = resolution_data[resolution];
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(ds18b20->bus, tx_buffer, sizeof(tx_buffer)), TAG, "send new resolution failed");
ds18b20->resolution = resolution;
return ESP_OK;
}
esp_err_t ds18b20_trigger_temperature_conversion(ds18b20_device_handle_t ds18b20)
{
ESP_RETURN_ON_FALSE(ds18b20, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// reset bus and check if the ds18b20 is present
ESP_RETURN_ON_ERROR(onewire_bus_reset(ds18b20->bus), TAG, "reset bus error");
// send command: DS18B20_CMD_CONVERT_TEMP
ESP_RETURN_ON_ERROR(ds18b20_send_command(ds18b20, DS18B20_CMD_CONVERT_TEMP), TAG, "send DS18B20_CMD_CONVERT_TEMP failed");
// delay proper time for temperature conversion
const uint32_t delays_ms[] = {100, 200, 400, 800};
vTaskDelay(pdMS_TO_TICKS(delays_ms[ds18b20->resolution]));
return ESP_OK;
}
esp_err_t ds18b20_get_temperature(ds18b20_device_handle_t ds18b20, float *ret_temperature)
{
ESP_RETURN_ON_FALSE(ds18b20 && ret_temperature, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// reset bus and check if the ds18b20 is present
ESP_RETURN_ON_ERROR(onewire_bus_reset(ds18b20->bus), TAG, "reset bus error");
// send command: DS18B20_CMD_READ_SCRATCHPAD
ESP_RETURN_ON_ERROR(ds18b20_send_command(ds18b20, DS18B20_CMD_READ_SCRATCHPAD), TAG, "send DS18B20_CMD_READ_SCRATCHPAD failed");
// read scratchpad data
ds18b20_scratchpad_t scratchpad;
ESP_RETURN_ON_ERROR(onewire_bus_read_bytes(ds18b20->bus, (uint8_t *)&scratchpad, sizeof(scratchpad)),
TAG, "error while reading scratchpad data");
// check crc
ESP_RETURN_ON_FALSE(onewire_crc8(0, (uint8_t *)&scratchpad, 8) == scratchpad.crc_value, ESP_ERR_INVALID_CRC, TAG, "scratchpad crc error");
const uint8_t lsb_mask[4] = {0x07, 0x03, 0x01, 0x00}; // mask bits not used in low resolution
uint8_t lsb_masked = scratchpad.temp_lsb & (~lsb_mask[scratchpad.configuration >> 5]);
// Combine the MSB and masked LSB into a signed 16-bit integer
int16_t temperature_raw = (((int16_t)scratchpad.temp_msb << 8) | lsb_masked);
// Convert the raw temperature to a float,
*ret_temperature = temperature_raw / 16.0f;
return ESP_OK;
}

Binary file not shown.

View File

@ -0,0 +1,3 @@
## 1.0.0
- Initial driver version, with the RMT driver as backend controller

View File

@ -0,0 +1,6 @@
idf_component_register(SRCS "src/onewire_bus_api.c"
"src/onewire_bus_impl_rmt.c"
"src/onewire_crc.c"
"src/onewire_device.c"
INCLUDE_DIRS "include" "interface"
PRIV_REQUIRES driver)

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,5 @@
# Dallas 1-Wire Bus Driver
[![Component Registry](https://components.espressif.com/components/espressif/onewire_bus/badge.svg)](https://components.espressif.com/components/espressif/onewire_bus)
This directory contains an implementation for Dallas 1-Wire bus by different peripherals. Currently only RMT is supported as the backend.

View File

@ -0,0 +1,7 @@
dependencies:
idf:
version: '>=5.0'
description: Driver for Dalas 1-Wire bus
issues: https://github.com/espressif/idf-extra-components/issues
url: https://github.com/espressif/idf-extra-components/tree/master/onewire_bus
version: 1.0.1

View File

@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "onewire_types.h"
#include "onewire_bus_impl_rmt.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Write bytes to 1-wire bus
*
* @param[in] bus 1-Wire bus handle
* @param[in] tx_data pointer to data to be sent
* @param[in] tx_data_size size of data to be sent, in bytes
* @return
* - ESP_OK: Write bytes to 1-Wire bus successfully
* - ESP_ERR_INVALID_ARG: Write bytes to 1-Wire bus failed because of invalid argument
* - ESP_FAIL: Write bytes to 1-Wire bus failed because of other errors
*/
esp_err_t onewire_bus_write_bytes(onewire_bus_handle_t bus, const uint8_t *tx_data, uint8_t tx_data_size);
/**
* @brief Read bytes from 1-wire bus
*
* @param[in] bus 1-wire bus handle
* @param[out] rx_buf pointer to buffer to store received data
* @param[in] rx_buf_size size of buffer to store received data, in bytes
* @return
* - ESP_OK: Read bytes from 1-Wire bus successfully
* - ESP_ERR_INVALID_ARG: Read bytes from 1-Wire bus failed because of invalid argument
* - ESP_FAIL: Read bytes from 1-Wire bus failed because of other errors
*/
esp_err_t onewire_bus_read_bytes(onewire_bus_handle_t bus, uint8_t *rx_buf, size_t rx_buf_size);
/**
* @brief Write a bit to 1-wire bus, this is a blocking function
*
* @param[in] handle 1-wire bus handle
* @param[in] tx_bit bit to transmit, 0 for zero bit, other for one bit
* @return
* - ESP_OK Write bit to 1-wire bus successfully.
* - ESP_ERR_INVALID_ARG Invalid argument.
*/
esp_err_t onewire_bus_write_bit(onewire_bus_handle_t bus, uint8_t tx_bit);
/**
* @brief Read a bit from 1-wire bus
*
* @param[in] handle 1-wire bus handle
* @param[out] rx_bit received bit, 0 for zero bit, 1 for one bit
* @return
* - ESP_OK Read bit from 1-wire bus successfully.
* - ESP_ERR_INVALID_ARG Invalid argument.
*/
esp_err_t onewire_bus_read_bit(onewire_bus_handle_t bus, uint8_t *rx_bit);
/**
* @brief Send reset pulse to the bus, and check if there are devices attached to the bus
*
* @param[in] bus 1-Wire bus handle
*
* @return
* - ESP_OK: Reset 1-Wire bus successfully and find device on the bus
* - ESP_ERR_NOT_FOUND: Reset 1-Wire bus successfully but no device found on the bus
* - ESP_FAIL: Reset 1-Wire bus failed because of other errors
*/
esp_err_t onewire_bus_reset(onewire_bus_handle_t bus);
/**
* @brief Free 1-Wire bus resources
*
* @param[in] bus 1-Wire bus handle
*
* @return
* - ESP_OK: Free resources successfully
* - ESP_FAIL: Free resources failed because error occurred
*/
esp_err_t onewire_bus_del(onewire_bus_handle_t bus);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "onewire_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief 1-Wire bus RMT specific configuration
*/
typedef struct {
uint32_t max_rx_bytes; /*!< Set the largest possible single receive size,
which determins the size of the internal buffer that used to save the receiving RMT symbols */
} onewire_bus_rmt_config_t;
/**
* @brief Create 1-Wire bus with RMT backend
*
* @note One 1-Wire bus utilizes a pair of RMT TX and RX channels
*
* @param[in] bus_config 1-Wire bus configuration
* @param[in] rmt_config RMT specific configuration
* @param[out] ret_bus Returned 1-Wire bus handle
* @return
* - ESP_OK: create 1-Wire bus handle successfully
* - ESP_ERR_INVALID_ARG: create 1-Wire bus handle failed because of invalid argument
* - ESP_ERR_NO_MEM: create 1-Wire bus handle failed because of out of memory
* - ESP_FAIL: create 1-Wire bus handle failed because some other error
*/
esp_err_t onewire_new_bus_rmt(const onewire_bus_config_t *bus_config, const onewire_bus_rmt_config_t *rmt_config, onewire_bus_handle_t *ret_bus);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,12 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#define ONEWIRE_CMD_SEARCH_NORMAL 0xF0
#define ONEWIRE_CMD_MATCH_ROM 0x55
#define ONEWIRE_CMD_SKIP_ROM 0xCC
#define ONEWIRE_CMD_SEARCH_ALARM 0xEC
#define ONEWIRE_CMD_READ_POWER_SUPPLY 0xB4

View File

@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Calculate Dallas CRC8 value of a given buffer
*
* @param[in] init_crc Initial CRC value
* @param[in] input Input buffer to calculate CRC value
* @param[in] input_size Size of input buffer, in bytes
* @return CRC8 result of the input buffer
*/
uint8_t onewire_crc8(uint8_t init_crc, uint8_t *input, size_t input_size);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "onewire_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief 1-Wire device generic type
*/
typedef struct onewire_device_t {
onewire_bus_handle_t bus; /*!< Which bus the 1-Wire device is attached to */
onewire_device_address_t address; /*!< Device address (represented by its internal ROM ID) */
} onewire_device_t;
/**
* @brief Create an iterator to enumerate the 1-Wire devices on the bus
*
* @param[in] bus 1-Wire bus handle
* @param[out] ret_iter Returned created device iterator
* @return
* - ESP_OK: Create device iterator successfully
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NO_MEM: No memory to create device iterator
* - ESP_FAIL: Other errors
*/
esp_err_t onewire_new_device_iter(onewire_bus_handle_t bus, onewire_device_iter_handle_t *ret_iter);
/**
* @brief Delete the device iterator
*
* @param[in] iter Device iterator handle
* @return
* - ESP_OK: Delete device iterator successfully
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_FAIL: Other errors
*/
esp_err_t onewire_del_device_iter(onewire_device_iter_handle_t iter);
/**
* @brief Get the next 1-Wire device from the iterator
*
* @param[in] iter Device iterator handle
* @param[out] dev Returned 1-Wire device handle
* @return
* - ESP_OK: Get next device successfully
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NOT_FOUND: No more device to get
* - ESP_FAIL: Other errors
*/
esp_err_t onewire_device_iter_get_next(onewire_device_iter_handle_t iter, onewire_device_t *dev);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Type of 1-Wire bus handle
*/
typedef struct onewire_bus_t *onewire_bus_handle_t;
/**
* @brief Type of the address for a 1-Wire compatible device
*/
typedef uint64_t onewire_device_address_t;
/**
* @brief Type of 1-Wire device iterator handle
*/
typedef struct onewire_device_iter_t *onewire_device_iter_handle_t;
/**
* @brief 1-Wire bus configuration
*/
typedef struct {
int bus_gpio_num; /*!< GPIO number that used by the 1-Wire bus */
} onewire_bus_config_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,97 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct onewire_bus_t onewire_bus_t; /*!< Type of 1-Wire bus */
/**
* @brief 1-Wire bus interface definition
*/
struct onewire_bus_t {
/**
* @brief Write bytes to 1-wire bus
*
* @note This is a blocking function
*
* @param[in] bus 1-Wire bus handle
* @param[in] tx_data pointer to data to be sent
* @param[in] tx_data_size size of data to be sent, in bytes
* @return
* - ESP_OK: Write bytes to 1-Wire bus successfully
* - ESP_ERR_INVALID_ARG: Write bytes to 1-Wire bus failed because of invalid argument
* - ESP_FAIL: Write bytes to 1-Wire bus failed because of other errors
*/
esp_err_t (*write_bytes)(onewire_bus_t *bus, const uint8_t *tx_data, uint8_t tx_data_size);
/**
* @brief Read bytes from 1-wire bus
*
* @param[in] bus 1-wire bus handle
* @param[out] rx_buf pointer to buffer to store received data
* @param[in] rx_buf_size size of buffer to store received data, in bytes
* @return
* - ESP_OK: Read bytes from 1-Wire bus successfully
* - ESP_ERR_INVALID_ARG: Read bytes from 1-Wire bus failed because of invalid argument
* - ESP_FAIL: Read bytes from 1-Wire bus failed because of other errors
*/
esp_err_t (*read_bytes)(onewire_bus_t *bus, uint8_t *rx_buf, size_t rx_buf_size);
/**
* @brief Write a bit to 1-wire bus, this is a blocking function
*
* @param[in] handle 1-wire bus handle
* @param[in] tx_bit bit to transmit, 0 for zero bit, other for one bit
* @return
* - ESP_OK Write bit to 1-wire bus successfully.
* - ESP_ERR_INVALID_ARG Invalid argument.
*/
esp_err_t (*write_bit)(onewire_bus_handle_t handle, uint8_t tx_bit);
/**
* @brief Read a bit from 1-wire bus
*
* @param[in] handle 1-wire bus handle
* @param[out] rx_bit received bit, 0 for zero bit, 1 for one bit
* @return
* - ESP_OK Read bit from 1-wire bus successfully.
* - ESP_ERR_INVALID_ARG Invalid argument.
*/
esp_err_t (*read_bit)(onewire_bus_handle_t handle, uint8_t *rx_bit);
/**
* @brief Send reset pulse to the bus, and check if there are devices attached to the bus
*
* @param[in] bus 1-Wire bus handle
*
* @return
* - ESP_OK: Reset 1-Wire bus successfully and find device on the bus
* - ESP_ERR_NOT_FOUND: Reset 1-Wire bus successfully but no device found on the bus
* - ESP_FAIL: Reset 1-Wire bus failed because of other errors
*/
esp_err_t (*reset)(onewire_bus_t *bus);
/**
* @brief Free 1-Wire bus resources
*
* @param[in] bus 1-Wire bus handle
*
* @return
* - ESP_OK: Free resources successfully
* - ESP_FAIL: Free resources failed because error occurred
*/
esp_err_t (*del)(onewire_bus_t *bus);
};
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_log.h"
#include "esp_check.h"
#include "onewire_types.h"
#include "onewire_bus_interface.h"
static const char *TAG = "1-wire";
esp_err_t onewire_bus_reset(onewire_bus_handle_t bus)
{
ESP_RETURN_ON_FALSE(bus, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return bus->reset(bus);
}
esp_err_t onewire_bus_write_bytes(onewire_bus_handle_t bus, const uint8_t *tx_data, uint8_t tx_data_size)
{
ESP_RETURN_ON_FALSE(bus && tx_data && tx_data_size, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return bus->write_bytes(bus, tx_data, tx_data_size);
}
esp_err_t onewire_bus_read_bytes(onewire_bus_handle_t bus, uint8_t *rx_buf, size_t rx_buf_size)
{
ESP_RETURN_ON_FALSE(bus && rx_buf && rx_buf_size, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return bus->read_bytes(bus, rx_buf, rx_buf_size);
}
esp_err_t onewire_bus_write_bit(onewire_bus_handle_t bus, uint8_t tx_bit)
{
ESP_RETURN_ON_FALSE(bus, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return bus->write_bit(bus, tx_bit);
}
esp_err_t onewire_bus_read_bit(onewire_bus_handle_t bus, uint8_t *rx_bit)
{
ESP_RETURN_ON_FALSE(bus && rx_bit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return bus->read_bit(bus, rx_bit);
}
esp_err_t onewire_bus_del(onewire_bus_handle_t bus)
{
ESP_RETURN_ON_FALSE(bus, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
return bus->del(bus);
}

View File

@ -0,0 +1,495 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_check.h"
#include "esp_attr.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "onewire_bus_impl_rmt.h"
#include "onewire_bus_interface.h"
static const char *TAG = "1-wire.rmt";
#define ONEWIRE_RMT_RESOLUTION_HZ 1000000 // RMT channel default resolution for 1-wire bus, 1MHz, 1tick = 1us
#define ONEWIRE_RMT_DEFAULT_TRANS_QUEUE_SIZE 4
// the memory size of each RMT channel, in words (4 bytes)
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define ONEWIRE_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
#else
#define ONEWIRE_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
#endif
// for chips whose RMT RX channel doesn't support ping-pong, we need the user to tell the maximum number of bytes will be received
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
// one RMT symbol represents one bit, so x8
#define ONEWIRE_RMT_RX_MEM_BLOCK_SIZE (rmt_config->max_rx_bytes * 8)
#else // otherwise, we just use one memory block, to save resources
#define ONEWIRE_RMT_RX_MEM_BLOCK_SIZE ONEWIRE_RMT_DEFAULT_MEM_BLOCK_SYMBOLS
#endif
/*
Reset Pulse:
| RESET_PULSE | RESET_WAIT_DURATION |
| _DURATION | |
| | | | RESET | |
| | * | | _PRESENCE | |
| | | | _DURATION | |
----------+ +-----+ +--------------
| | | |
| | | |
| | | |
+-------------+ +-----------+
*: RESET_PRESENCE_WAIT_DURATION
*/
#define ONEWIRE_RESET_PULSE_DURATION 500 // duration of reset bit
#define ONEWIRE_RESET_WAIT_DURATION 200 // how long should master wait for device to show its presence
#define ONEWIRE_RESET_PRESENCE_WAIT_DURATION_MIN 15 // minimum duration for master to wait device to show its presence
#define ONEWIRE_RESET_PRESENCE_DURATION_MIN 60 // minimum duration for master to recognize device as present
/*
Write 1 bit:
| SLOT_START | SLOT_BIT | SLOT_RECOVERY | NEXT
| _DURATION | _DURATION | _DURATION | SLOT
| | | |
----------+ +-------------------------------------
| |
| |
| |
+------------+
Write 0 bit:
| SLOT_START | SLOT_BIT | SLOT_RECOVERY | NEXT
| _DURATION | _DURATION | _DURATION | SLOT
| | | |
----------+ +-------------------------
| |
| |
| |
+------------------------+
Read 1 bit:
| SLOT_START | SLOT_BIT_DURATION | SLOT_RECOVERY | NEXT
| _DURATION | | _DURATION | SLOT
| | SLOT_BIT_ | | |
| | SAMPLE_TIME | | |
----------+ +----------------------------------------------
| |
| |
| |
+------------+
Read 0 bit:
| SLOT_START | SLOT_BIT_DURATION | SLOT_RECOVERY | NEXT
| _DURATION | | _DURATION | SLOT
| | SLOT_BIT_ | | |
| | SAMPLE_TIME | | |
----------+ | | +-----------------------------
| | |
| | PULLED DOWN |
| | BY DEVICE |
+-----------------------------+
*/
#define ONEWIRE_SLOT_START_DURATION 2 // bit start pulse duration
#define ONEWIRE_SLOT_BIT_DURATION 60 // duration for each bit to transmit
// refer to https://www.maximintegrated.com/en/design/technical-documents/app-notes/3/3829.html for more information
#define ONEWIRE_SLOT_RECOVERY_DURATION 2 // recovery time between each bit, should be longer in parasite power mode
#define ONEWIRE_SLOT_BIT_SAMPLE_TIME 15 // how long after bit start pulse should the master sample from the bus
typedef struct {
onewire_bus_t base; /*!< base class */
rmt_channel_handle_t tx_channel; /*!< rmt tx channel handler */
rmt_channel_handle_t rx_channel; /*!< rmt rx channel handler */
rmt_encoder_handle_t tx_bytes_encoder; /*!< used to encode commands and data */
rmt_encoder_handle_t tx_copy_encoder; /*!< used to encode reset pulse and bits */
rmt_symbol_word_t *rx_symbols_buf; /*!< hold rmt raw symbols */
size_t max_rx_bytes; /*!< buffer size in byte for single receive transaction */
QueueHandle_t receive_queue;
SemaphoreHandle_t bus_mutex;
} onewire_bus_rmt_obj_t;
static rmt_symbol_word_t onewire_reset_pulse_symbol = {
.level0 = 0,
.duration0 = ONEWIRE_RESET_PULSE_DURATION,
.level1 = 1,
.duration1 = ONEWIRE_RESET_WAIT_DURATION
};
static rmt_symbol_word_t onewire_bit0_symbol = {
.level0 = 0,
.duration0 = ONEWIRE_SLOT_START_DURATION + ONEWIRE_SLOT_BIT_DURATION,
.level1 = 1,
.duration1 = ONEWIRE_SLOT_RECOVERY_DURATION
};
static rmt_symbol_word_t onewire_bit1_symbol = {
.level0 = 0,
.duration0 = ONEWIRE_SLOT_START_DURATION,
.level1 = 1,
.duration1 = ONEWIRE_SLOT_BIT_DURATION + ONEWIRE_SLOT_RECOVERY_DURATION
};
const static rmt_transmit_config_t onewire_rmt_tx_config = {
.loop_count = 0, // no transfer loop
.flags.eot_level = 1 // onewire bus should be released in IDLE
};
const static rmt_receive_config_t onewire_rmt_rx_config = {
.signal_range_min_ns = 1000000000 / ONEWIRE_RMT_RESOLUTION_HZ,
.signal_range_max_ns = (ONEWIRE_RESET_PULSE_DURATION + ONEWIRE_RESET_WAIT_DURATION) * 1000,
};
static esp_err_t onewire_bus_rmt_read_bit(onewire_bus_handle_t bus, uint8_t *rx_bit);
static esp_err_t onewire_bus_rmt_write_bit(onewire_bus_handle_t bus, uint8_t tx_bit);
static esp_err_t onewire_bus_rmt_read_bytes(onewire_bus_handle_t bus, uint8_t *rx_buf, size_t rx_buf_size);
static esp_err_t onewire_bus_rmt_write_bytes(onewire_bus_handle_t bus, const uint8_t *tx_data, uint8_t tx_data_size);
static esp_err_t onewire_bus_rmt_reset(onewire_bus_handle_t bus);
static esp_err_t onewire_bus_rmt_del(onewire_bus_handle_t bus);
static esp_err_t onewire_bus_rmt_destroy(onewire_bus_rmt_obj_t *bus_rmt);
IRAM_ATTR
bool onewire_rmt_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
{
BaseType_t task_woken = pdFALSE;
onewire_bus_rmt_obj_t *bus_rmt = (onewire_bus_rmt_obj_t *)user_data;
xQueueSendFromISR(bus_rmt->receive_queue, edata, &task_woken);
return task_woken;
}
/*
[0].0 means symbol[0].duration0
First reset pulse after rmt channel init:
Bus is low | Reset | Wait | Device | Bus Idle
after init | Pulse | | Presence |
+------+ +-----------
| | |
| | |
| | |
-------------------+ +----------+
1 2 3
[0].1 [0].0 [1].1 [1].0
Following reset pulses:
Bus is high | Reset | Wait | Device | Bus Idle
after init | Pulse | | Presence |
------------+ +------+ +-----------
| | | |
| | | |
| | | |
+-------+ +----------+
1 2 3 4
[0].0 [0].1 [1].0 [1].1
*/
static bool onewire_rmt_check_presence_pulse(rmt_symbol_word_t *rmt_symbols, size_t symbol_num)
{
bool ret = false;
if (symbol_num >= 2) { // there should be at lease 2 symbols(3 or 4 edges)
if (rmt_symbols[0].level1 == 1) { // bus is high before reset pulse
if (rmt_symbols[0].duration1 > ONEWIRE_RESET_PRESENCE_WAIT_DURATION_MIN &&
rmt_symbols[1].duration0 > ONEWIRE_RESET_PRESENCE_DURATION_MIN) {
ret = true;
}
} else { // bus is low before reset pulse(first pulse after rmt channel init)
if (rmt_symbols[0].duration0 > ONEWIRE_RESET_PRESENCE_WAIT_DURATION_MIN &&
rmt_symbols[1].duration1 > ONEWIRE_RESET_PRESENCE_DURATION_MIN) {
ret = true;
}
}
}
return ret;
}
static void onewire_rmt_decode_data(rmt_symbol_word_t *rmt_symbols, size_t symbol_num, uint8_t *rx_buf, size_t rx_buf_size)
{
size_t byte_pos = 0;
size_t bit_pos = 0;
for (size_t i = 0; i < symbol_num; i ++) {
if (rmt_symbols[i].duration0 > ONEWIRE_SLOT_BIT_SAMPLE_TIME) { // 0 bit
rx_buf[byte_pos] &= ~(1 << bit_pos); // LSB first
} else { // 1 bit
rx_buf[byte_pos] |= 1 << bit_pos;
}
bit_pos ++;
if (bit_pos >= 8) {
bit_pos = 0;
byte_pos ++;
if (byte_pos >= rx_buf_size) {
break;
}
}
}
}
esp_err_t onewire_new_bus_rmt(const onewire_bus_config_t *bus_config, const onewire_bus_rmt_config_t *rmt_config, onewire_bus_handle_t *ret_bus)
{
esp_err_t ret = ESP_OK;
onewire_bus_rmt_obj_t *bus_rmt = NULL;
ESP_RETURN_ON_FALSE(bus_config && rmt_config && ret_bus, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
bus_rmt = calloc(1, sizeof(onewire_bus_rmt_obj_t));
ESP_RETURN_ON_FALSE(bus_rmt, ESP_ERR_NO_MEM, TAG, "no mem for onewire_bus_rmt_obj_t");
// create rmt bytes encoder to transmit 1-wire commands and data
rmt_bytes_encoder_config_t bytes_encoder_config = {
.bit0 = onewire_bit0_symbol,
.bit1 = onewire_bit1_symbol,
.flags.msb_first = 0,
};
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &bus_rmt->tx_bytes_encoder),
err, TAG, "create bytes encoder failed");
// create rmt copy encoder to transmit 1-wire reset pulse or bits
rmt_copy_encoder_config_t copy_encoder_config = {};
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(&copy_encoder_config, &bus_rmt->tx_copy_encoder),
err, TAG, "create copy encoder failed");
// Note: must create rmt rx channel before tx channel
rmt_rx_channel_config_t onewire_rx_channel_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = ONEWIRE_RMT_RESOLUTION_HZ,
.gpio_num = bus_config->bus_gpio_num,
.mem_block_symbols = ONEWIRE_RMT_RX_MEM_BLOCK_SIZE,
};
ESP_GOTO_ON_ERROR(rmt_new_rx_channel(&onewire_rx_channel_cfg, &bus_rmt->rx_channel),
err, TAG, "create rmt rx channel failed");
// create rmt tx channel
rmt_tx_channel_config_t onewire_tx_channel_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = ONEWIRE_RMT_RESOLUTION_HZ,
.gpio_num = bus_config->bus_gpio_num,
.mem_block_symbols = ONEWIRE_RMT_DEFAULT_MEM_BLOCK_SYMBOLS,
.trans_queue_depth = ONEWIRE_RMT_DEFAULT_TRANS_QUEUE_SIZE,
.flags.io_loop_back = true, // make tx channel coexist with rx channel on the same gpio pin
.flags.io_od_mode = true, // enable open-drain mode for 1-wire bus
};
ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&onewire_tx_channel_cfg, &bus_rmt->tx_channel),
err, TAG, "create rmt tx channel failed");
// allocate rmt rx symbol buffer, one RMT symbol represents one bit, so x8
bus_rmt->rx_symbols_buf = malloc(rmt_config->max_rx_bytes * sizeof(rmt_symbol_word_t) * 8);
ESP_GOTO_ON_FALSE(bus_rmt->rx_symbols_buf, ESP_ERR_NO_MEM, err, TAG, "no mem to store received RMT symbols");
bus_rmt->max_rx_bytes = rmt_config->max_rx_bytes;
bus_rmt->receive_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t));
ESP_GOTO_ON_FALSE(bus_rmt->receive_queue, ESP_ERR_NO_MEM, err, TAG, "receive queue creation failed");
bus_rmt->bus_mutex = xSemaphoreCreateMutex();
ESP_GOTO_ON_FALSE(bus_rmt->bus_mutex, ESP_ERR_NO_MEM, err, TAG, "bus mutex creation failed");
// register rmt rx done callback
rmt_rx_event_callbacks_t cbs = {
.on_recv_done = onewire_rmt_rx_done_callback
};
ESP_GOTO_ON_ERROR(rmt_rx_register_event_callbacks(bus_rmt->rx_channel, &cbs, bus_rmt),
err, TAG, "enable rmt rx channel failed");
// enable rmt channels
ESP_GOTO_ON_ERROR(rmt_enable(bus_rmt->rx_channel), err, TAG, "enable rmt rx channel failed");
ESP_GOTO_ON_ERROR(rmt_enable(bus_rmt->tx_channel), err, TAG, "enable rmt tx channel failed");
// release the bus by sending a special RMT symbol
static rmt_symbol_word_t release_symbol = {
.level0 = 1,
.duration0 = 1,
.level1 = 1,
.duration1 = 0,
};
ESP_GOTO_ON_ERROR(rmt_transmit(bus_rmt->tx_channel, bus_rmt->tx_copy_encoder, &release_symbol,
sizeof(release_symbol), &onewire_rmt_tx_config), err, TAG, "release bus failed");
bus_rmt->base.del = onewire_bus_rmt_del;
bus_rmt->base.reset = onewire_bus_rmt_reset;
bus_rmt->base.write_bit = onewire_bus_rmt_write_bit;
bus_rmt->base.write_bytes = onewire_bus_rmt_write_bytes;
bus_rmt->base.read_bit = onewire_bus_rmt_read_bit;
bus_rmt->base.read_bytes = onewire_bus_rmt_read_bytes;
*ret_bus = &bus_rmt->base;
return ret;
err:
if (bus_rmt) {
onewire_bus_rmt_destroy(bus_rmt);
}
return ret;
}
static esp_err_t onewire_bus_rmt_destroy(onewire_bus_rmt_obj_t *bus_rmt)
{
if (bus_rmt->tx_bytes_encoder) {
rmt_del_encoder(bus_rmt->tx_bytes_encoder);
}
if (bus_rmt->tx_copy_encoder) {
rmt_del_encoder(bus_rmt->tx_copy_encoder);
}
if (bus_rmt->rx_channel) {
rmt_disable(bus_rmt->rx_channel);
rmt_del_channel(bus_rmt->rx_channel);
}
if (bus_rmt->tx_channel) {
rmt_disable(bus_rmt->tx_channel);
rmt_del_channel(bus_rmt->tx_channel);
}
if (bus_rmt->receive_queue) {
vQueueDelete(bus_rmt->receive_queue);
}
if (bus_rmt->bus_mutex) {
vSemaphoreDelete(bus_rmt->bus_mutex);
}
if (bus_rmt->rx_symbols_buf) {
free(bus_rmt->rx_symbols_buf);
}
free(bus_rmt);
return ESP_OK;
}
static esp_err_t onewire_bus_rmt_del(onewire_bus_handle_t bus)
{
onewire_bus_rmt_obj_t *bus_rmt = __containerof(bus, onewire_bus_rmt_obj_t, base);
return onewire_bus_rmt_destroy(bus_rmt);
}
static esp_err_t onewire_bus_rmt_reset(onewire_bus_handle_t bus)
{
onewire_bus_rmt_obj_t *bus_rmt = __containerof(bus, onewire_bus_rmt_obj_t, base);
esp_err_t ret = ESP_OK;
xSemaphoreTake(bus_rmt->bus_mutex, portMAX_DELAY);
// send reset pulse while receive presence pulse
ESP_GOTO_ON_ERROR(rmt_receive(bus_rmt->rx_channel, bus_rmt->rx_symbols_buf, sizeof(rmt_symbol_word_t) * 2, &onewire_rmt_rx_config),
err, TAG, "1-wire reset pulse receive failed");
ESP_GOTO_ON_ERROR(rmt_transmit(bus_rmt->tx_channel, bus_rmt->tx_copy_encoder, &onewire_reset_pulse_symbol, sizeof(onewire_reset_pulse_symbol), &onewire_rmt_tx_config),
err, TAG, "1-wire reset pulse transmit failed");
// wait and check presence pulse
rmt_rx_done_event_data_t rmt_rx_evt_data;
ESP_GOTO_ON_FALSE(xQueueReceive(bus_rmt->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS,
ESP_ERR_TIMEOUT, err, TAG, "1-wire reset pulse receive timeout");
if (onewire_rmt_check_presence_pulse(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols) == false) {
ret = ESP_ERR_NOT_FOUND;
}
err:
xSemaphoreGive(bus_rmt->bus_mutex);
return ret;
}
static esp_err_t onewire_bus_rmt_write_bytes(onewire_bus_handle_t bus, const uint8_t *tx_data, uint8_t tx_data_size)
{
onewire_bus_rmt_obj_t *bus_rmt = __containerof(bus, onewire_bus_rmt_obj_t, base);
esp_err_t ret = ESP_OK;
xSemaphoreTake(bus_rmt->bus_mutex, portMAX_DELAY);
// transmit data with the bytes encoder
ESP_GOTO_ON_ERROR(rmt_transmit(bus_rmt->tx_channel, bus_rmt->tx_bytes_encoder, tx_data, tx_data_size, &onewire_rmt_tx_config),
err, TAG, "1-wire data transmit failed");
// wait the transmission to complete
ESP_GOTO_ON_ERROR(rmt_tx_wait_all_done(bus_rmt->tx_channel, 50), err, TAG, "wait for 1-wire data transmit failed");
err:
xSemaphoreGive(bus_rmt->bus_mutex);
return ret;
}
// While receiving data, we use rmt transmit channel to send 0xFF to generate read pulse,
// at the same time, receive channel is used to record weather the bus is pulled down by device.
static esp_err_t onewire_bus_rmt_read_bytes(onewire_bus_handle_t bus, uint8_t *rx_buf, size_t rx_buf_size)
{
onewire_bus_rmt_obj_t *bus_rmt = __containerof(bus, onewire_bus_rmt_obj_t, base);
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(rx_buf_size <= bus_rmt->max_rx_bytes, ESP_ERR_INVALID_ARG, TAG, "rx_buf_size too large for buffer to hold");
memset(rx_buf, 0, rx_buf_size);
xSemaphoreTake(bus_rmt->bus_mutex, portMAX_DELAY);
// transmit one bits to generate read clock
uint8_t tx_buffer[rx_buf_size];
memset(tx_buffer, 0xFF, rx_buf_size);
// transmit 1 bits while receiving
ESP_GOTO_ON_ERROR(rmt_receive(bus_rmt->rx_channel, bus_rmt->rx_symbols_buf, rx_buf_size * 8 * sizeof(rmt_symbol_word_t), &onewire_rmt_rx_config),
err, TAG, "1-wire data receive failed");
ESP_GOTO_ON_ERROR(rmt_transmit(bus_rmt->tx_channel, bus_rmt->tx_bytes_encoder, tx_buffer, sizeof(tx_buffer), &onewire_rmt_tx_config),
err, TAG, "1-wire data transmit failed");
// wait the transmission finishes and decode data
rmt_rx_done_event_data_t rmt_rx_evt_data;
ESP_GOTO_ON_FALSE(xQueueReceive(bus_rmt->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS, ESP_ERR_TIMEOUT,
err, TAG, "1-wire data receive timeout");
onewire_rmt_decode_data(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols, rx_buf, rx_buf_size);
err:
xSemaphoreGive(bus_rmt->bus_mutex);
return ret;
}
static esp_err_t onewire_bus_rmt_write_bit(onewire_bus_handle_t bus, uint8_t tx_bit)
{
onewire_bus_rmt_obj_t *bus_rmt = __containerof(bus, onewire_bus_rmt_obj_t, base);
const rmt_symbol_word_t *symbol_to_transmit = tx_bit ? &onewire_bit1_symbol : &onewire_bit0_symbol;
esp_err_t ret = ESP_OK;
xSemaphoreTake(bus_rmt->bus_mutex, portMAX_DELAY);
// transmit bit
ESP_GOTO_ON_ERROR(rmt_transmit(bus_rmt->tx_channel, bus_rmt->tx_copy_encoder, symbol_to_transmit, sizeof(rmt_symbol_word_t), &onewire_rmt_tx_config),
err, TAG, "1-wire bit transmit failed");
// wait the transmission to complete
ESP_GOTO_ON_ERROR(rmt_tx_wait_all_done(bus_rmt->tx_channel, 50), err, TAG, "wait for 1-wire bit transmit failed");
err:
xSemaphoreGive(bus_rmt->bus_mutex);
return ret;
}
static esp_err_t onewire_bus_rmt_read_bit(onewire_bus_handle_t bus, uint8_t *rx_bit)
{
onewire_bus_rmt_obj_t *bus_rmt = __containerof(bus, onewire_bus_rmt_obj_t, base);
esp_err_t ret = ESP_OK;
xSemaphoreTake(bus_rmt->bus_mutex, portMAX_DELAY);
// transmit 1 bit while receiving
ESP_GOTO_ON_ERROR(rmt_receive(bus_rmt->rx_channel, bus_rmt->rx_symbols_buf, sizeof(rmt_symbol_word_t), &onewire_rmt_rx_config),
err, TAG, "1-wire bit receive failed");
ESP_GOTO_ON_ERROR(rmt_transmit(bus_rmt->tx_channel, bus_rmt->tx_copy_encoder, &onewire_bit1_symbol, sizeof(rmt_symbol_word_t), &onewire_rmt_tx_config),
err, TAG, "1-wire bit transmit failed");
// wait the transmission finishes and decode data
rmt_rx_done_event_data_t rmt_rx_evt_data;
ESP_GOTO_ON_FALSE(xQueueReceive(bus_rmt->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS, ESP_ERR_TIMEOUT,
err, TAG, "1-wire bit receive timeout");
uint8_t rx_buffer = 0;
onewire_rmt_decode_data(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols, &rx_buffer, sizeof(rx_buffer));
*rx_bit = rx_buffer & 0x01;
err:
xSemaphoreGive(bus_rmt->bus_mutex);
return ret;
}

View File

@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "onewire_crc.h"
#define FAST_CRC 1 // define this to use the fast CRC table
#if FAST_CRC
static const uint8_t dalas_crc8_table[] = {
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
};
uint8_t onewire_crc8(uint8_t init_crc, uint8_t *input, size_t input_size)
{
uint8_t crc = init_crc;
for (size_t i = 0; i < input_size; i ++) {
crc = dalas_crc8_table[crc ^ input[i]];
}
return crc;
}
#else // FAST_CRC
uint8_t onewire_crc8(uint8_t init_crc, uint8_t *input, size_t input_size)
{
uint8_t crc = init_crc;
for (size_t i = 0; i < input_size; i++) {
uint8_t byte = input[i];
for (int j = 0; j < 8; j++) {
uint8_t x = (byte ^ crc) & 0x01;
crc >>= 1;
if (x != 0) {
crc ^= 0x8C;
}
byte >>= 1;
}
}
return crc;
}
#endif // FAST_CRC

View File

@ -0,0 +1,124 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdbool.h>
#include "esp_check.h"
#include "esp_log.h"
#include "onewire_bus.h"
#include "onewire_device.h"
#include "onewire_crc.h"
#include "onewire_cmd.h"
static const char *TAG = "1-wire.device";
typedef struct onewire_device_iter_t {
onewire_bus_handle_t bus;
uint16_t last_discrepancy;
bool is_last_device;
uint8_t rom_number[sizeof(onewire_device_address_t)];
} onewire_device_iter_t;
esp_err_t onewire_new_device_iter(onewire_bus_handle_t bus, onewire_device_iter_handle_t *ret_iter)
{
ESP_RETURN_ON_FALSE(bus && ret_iter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
onewire_device_iter_t *iter = calloc(1, sizeof(onewire_device_iter_t));
ESP_RETURN_ON_FALSE(iter, ESP_ERR_NO_MEM, TAG, "no mem for device iterator");
iter->bus = bus;
*ret_iter = iter;
return ESP_OK;
}
esp_err_t onewire_del_device_iter(onewire_device_iter_handle_t iter)
{
ESP_RETURN_ON_FALSE(iter, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
free(iter);
return ESP_OK;
}
// Search algorithm inspired by https://www.analog.com/en/app-notes/1wire-search-algorithm.html
esp_err_t onewire_device_iter_get_next(onewire_device_iter_handle_t iter, onewire_device_t *dev)
{
ESP_RETURN_ON_FALSE(iter && dev, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// we don't treat iterator ending and ESP_ERR_NOT_FOUND as an error condition, so just print debug message here
if (iter->is_last_device) {
ESP_LOGD(TAG, "1-wire rom search finished");
return ESP_ERR_NOT_FOUND;
}
onewire_bus_handle_t bus = iter->bus;
esp_err_t reset_result = onewire_bus_reset(bus);
if (reset_result == ESP_ERR_NOT_FOUND) {
ESP_LOGW(TAG, "reset bus failed: no devices found");
return ESP_ERR_NOT_FOUND;
}
ESP_RETURN_ON_ERROR(reset_result, TAG, "reset bus failed");
// send rom search command and start search algorithm
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(bus, (uint8_t[]) {
ONEWIRE_CMD_SEARCH_NORMAL
}, 1), TAG, "send ONEWIRE_CMD_SEARCH_NORMAL failed");
uint8_t last_zero = 0;
for (uint16_t rom_bit_index = 0; rom_bit_index < sizeof(onewire_device_address_t) * 8; rom_bit_index ++) {
uint8_t rom_byte_index = rom_bit_index / 8;
uint8_t rom_bit_mask = 1 << (rom_bit_index % 8); // calculate byte index and bit mask in advance for convenience
uint8_t rom_bit = 0;
uint8_t rom_bit_complement = 0;
ESP_RETURN_ON_ERROR(onewire_bus_read_bit(bus, &rom_bit), TAG, "read rom_bit error"); // write 1 bit to read from the bus
ESP_RETURN_ON_ERROR(onewire_bus_read_bit(bus, &rom_bit_complement), TAG, "read rom_bit_complement error"); // read a bit and its complement
// No devices participating in search.
if (rom_bit && rom_bit_complement) {
ESP_LOGE(TAG, "no devices participating in search");
return ESP_ERR_NOT_FOUND;
}
uint8_t search_direction;
if (rom_bit != rom_bit_complement) { // There are only 0s or 1s in the bit of the participating ROM numbers.
search_direction = rom_bit; // just go ahead
} else { // There are both 0s and 1s in the current bit position of the participating ROM numbers. This is a discrepancy.
if (rom_bit_index < iter->last_discrepancy) { // current id bit is before the last discrepancy bit
search_direction = (iter->rom_number[rom_byte_index] & rom_bit_mask) ? 0x01 : 0x00; // follow previous way
} else {
search_direction = (rom_bit_index == iter->last_discrepancy) ? 0x01 : 0x00; // search for 0 bit first
}
if (search_direction == 0) { // record zero's position in last zero
last_zero = rom_bit_index;
}
}
if (search_direction == 1) { // set corrsponding rom bit by search direction
iter->rom_number[rom_byte_index] |= rom_bit_mask;
} else {
iter->rom_number[rom_byte_index] &= ~rom_bit_mask;
}
// set search direction
ESP_RETURN_ON_ERROR(onewire_bus_write_bit(bus, search_direction), TAG, "write direction bit error");
}
// if the search was successful
iter->last_discrepancy = last_zero;
if (iter->last_discrepancy == 0) { // last zero loops back to the first bit
iter->is_last_device = true;
}
// check crc
ESP_RETURN_ON_FALSE(onewire_crc8(0, iter->rom_number, 7) == iter->rom_number[7], ESP_ERR_INVALID_CRC, TAG, "bad device crc");
// save the ROM number as the device address
memcpy(&dev->address, iter->rom_number, sizeof(onewire_device_address_t));
dev->bus = bus;
ESP_LOGD(TAG, "new 1-Wire device found, address: %016llX", dev->address);
return ESP_OK;
}

BIN
components/onewire/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(onewire)

View File

@ -0,0 +1,74 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
# Advanced RMT Transmit & Receive Example -- Simulate 1-Wire Bus
(See the README.md file in the upper level 'examples' directory for more information about examples.)
RMT peripheral has independent transmit and receive channels. We can simulate the [1-Wire](https://www.analog.com/en/technical-articles/guide-to-1wire-communication.html) bus by attaching a pair of transmit and receive channel to the same GPIO, and turning on the open-drain mode of the GPIO pad.
We've made the 1-Wire protocol implementation into a component called `onewire_bus`, which has been uploaded to the [component registry](https://components.espressif.com/components/espressif/onewire_bus).
This example demonstrates how to use that `onewire_bus` library to read temperature from the [DS18B20](https://www.analog.com/media/en/technical-documentation/data-sheets/ds18b20.pdf) sensor. Likewise, the DS18B20 device is also made as a single component and pushed to the [component registry](https://components.espressif.com/components/espressif/ds18b20).
One of the amazing feature that offered by the `onewire_bus` driver is that, is can support enumerate the devices on the bus, thus you can connect multiple DS18B20 sensors to the same bus and read their temperature one by one.
## How to Use Example
### Hardware Required
* A development board with any supported Espressif SOC chip (see `Supported Targets` table above)
* One or more DS18B20 sensors connected to the same bus by a 4.7 KΩ pull-up resistor
Connection :
```plain
┌──────────────────────────┐
│ 3.3V├───────┬─────────────┬──────────────────────┐
│ │ ┌┴┐ │VDD │VDD
│ ESP Board │ 4.7k│ │ ┌──────┴──────┐ ┌──────┴──────┐
│ │ └┬┘ DQ│ │ DQ│ │
│ ONEWIRE_GPIO_PIN├───────┴──┬───┤ DS18B20 │ ┌───┤ DS18B20 │ ......
│ │ └───│-------------│────┴───│-------------│──
│ │ └──────┬──────┘ └──────┬──────┘
│ │ │GND │GND
│ GND├─────────────────────┴──────────────────────┘
└──────────────────────────┘
```
The GPIO number used in this example can be changed according to your board, by the macro `EXAMPLE_ONEWIRE_BUS_GPIO` defined in [onewire_example_main.c](main/onewire_example_main.c).
> **Note**
> Parasite power mode is not supported, you have to connect VDD pin to make DS18B20 functional.
### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Console Output
```plain
I (340) main_task: Started on CPU0
I (350) main_task: Calling app_main()
I (350) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (360) gpio: GPIO[0]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0
I (370) example: 1-Wire bus installed on GPIO0
I (370) example: Device iterator created, start searching...
I (490) example: Found a DS18B20[0], address: 070822502019FC28
I (590) example: Found a DS18B20[1], address: FC0921C076034628
I (590) example: Searching done, 2 DS18B20 device(s) found
I (1620) example: temperature read from DS18B20[0]: 22.50C
I (2430) example: temperature read from DS18B20[1]: 22.81C
I (3440) example: temperature read from DS18B20[0]: 22.50C
I (4250) example: temperature read from DS18B20[1]: 22.81C
I (5260) example: temperature read from DS18B20[0]: 22.50C
I (6070) example: temperature read from DS18B20[1]: 22.81C
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "onewire_example_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,2 @@
dependencies:
ds18b20: "^0.1.0"

View File

@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_check.h"
#include "onewire_bus.h"
#include "ds18b20.h"
static const char *TAG = "example";
#define EXAMPLE_ONEWIRE_BUS_GPIO 0
#define EXAMPLE_ONEWIRE_MAX_DS18B20 2
void app_main(void)
{
// install new 1-wire bus
onewire_bus_handle_t bus;
onewire_bus_config_t bus_config = {
.bus_gpio_num = EXAMPLE_ONEWIRE_BUS_GPIO,
};
onewire_bus_rmt_config_t rmt_config = {
.max_rx_bytes = 10, // 1byte ROM command + 8byte ROM number + 1byte device command
};
ESP_ERROR_CHECK(onewire_new_bus_rmt(&bus_config, &rmt_config, &bus));
ESP_LOGI(TAG, "1-Wire bus installed on GPIO%d", EXAMPLE_ONEWIRE_BUS_GPIO);
int ds18b20_device_num = 0;
ds18b20_device_handle_t ds18b20s[EXAMPLE_ONEWIRE_MAX_DS18B20];
onewire_device_iter_handle_t iter = NULL;
onewire_device_t next_onewire_device;
esp_err_t search_result = ESP_OK;
// create 1-wire device iterator, which is used for device search
ESP_ERROR_CHECK(onewire_new_device_iter(bus, &iter));
ESP_LOGI(TAG, "Device iterator created, start searching...");
do {
search_result = onewire_device_iter_get_next(iter, &next_onewire_device);
if (search_result == ESP_OK) { // found a new device, let's check if we can upgrade it to a DS18B20
ds18b20_config_t ds_cfg = {};
if (ds18b20_new_device(&next_onewire_device, &ds_cfg, &ds18b20s[ds18b20_device_num]) == ESP_OK) {
ESP_LOGI(TAG, "Found a DS18B20[%d], address: %016llX", ds18b20_device_num, next_onewire_device.address);
ds18b20_device_num++;
if (ds18b20_device_num >= EXAMPLE_ONEWIRE_MAX_DS18B20) {
ESP_LOGI(TAG, "Max DS18B20 number reached, stop searching...");
break;
}
} else {
ESP_LOGI(TAG, "Found an unknown device, address: %016llX", next_onewire_device.address);
}
}
} while (search_result != ESP_ERR_NOT_FOUND);
ESP_ERROR_CHECK(onewire_del_device_iter(iter));
ESP_LOGI(TAG, "Searching done, %d DS18B20 device(s) found", ds18b20_device_num);
// set resolution for all DS18B20s
for (int i = 0; i < ds18b20_device_num; i++) {
// set resolution
ESP_ERROR_CHECK(ds18b20_set_resolution(ds18b20s[i], DS18B20_RESOLUTION_12B));
}
// get temperature from sensors one by one
float temperature;
while (1) {
vTaskDelay(pdMS_TO_TICKS(200));
for (int i = 0; i < ds18b20_device_num; i ++) {
ESP_ERROR_CHECK(ds18b20_trigger_temperature_conversion(ds18b20s[i]));
ESP_ERROR_CHECK(ds18b20_get_temperature(ds18b20s[i], &temperature));
ESP_LOGI(TAG, "temperature read from DS18B20[%d]: %.2fC", i, temperature);
}
}
}

View File

@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.esp32c3
@pytest.mark.esp32c6
@pytest.mark.esp32h2
@pytest.mark.generic
def test_onewire_example(dut: Dut) -> None:
dut.expect_exact('example: 1-Wire bus installed on GPIO')
dut.expect_exact('example: Device iterator created, start searching')
dut.expect_exact('example: Searching done')

View File

@ -119,11 +119,29 @@ esp_err_t zh_onewire_reset(void)
void zh_onewire_send_byte(uint8_t byte)
{
ESP_LOGI(TAG, "Onewire send byte begin.");
for (uint8_t i = 0; i < 8; ++i)
{
_send_bit(byte & 1);
byte >>= 1;
}
ESP_LOGI(TAG, "Onewire send byte success.");
}
uint8_t zh_onewire_read_byte(void)
{
ESP_LOGI(TAG, "Onewire read byte begin.");
uint8_t byte = 0;
for (uint8_t i = 0; i < 8; ++i)
{
byte >>= 1;
if (_read_bit() != 0)
{
byte |= 0x80;
}
}
ESP_LOGI(TAG, "Onewire read byte success.");
return byte;
}
esp_err_t zh_onewire_skip_rom(void)
@ -287,20 +305,6 @@ uint8_t *zh_onewire_search_rom_next(void)
return &_rom[0];
}
uint8_t zh_onewire_read_byte(void)
{
uint8_t byte = 0;
for (uint8_t i = 0; i < 8; ++i)
{
byte >>= 1;
if (_read_bit() != 0)
{
byte |= 0x80;
}
}
return byte;
}
static uint8_t _read_bit(void)
{
gpio_set_direction(_pin, GPIO_MODE_OUTPUT);