HB, OTA etc

This commit is contained in:
2026-04-22 20:11:55 +03:00
parent 4100931deb
commit cb1014c950
76 changed files with 3157 additions and 232 deletions

View File

@@ -0,0 +1,6 @@
onewire_bus/test_apps:
disable:
- if: CONFIG_NAME == "rmt" and SOC_RMT_SUPPORTED != 1
reason: RMT backend variant requires SOC RMT support
- if: CONFIG_NAME == "uart" and SOC_UART_SUPPORTED != 1
reason: UART backend variant requires SOC UART support

View File

@@ -0,0 +1 @@
d709015ba466095259228521cf1bad9c0cdaaa42a92ea5d9c88ec6c28ae89e9b

View File

@@ -0,0 +1,19 @@
## 1.1.0
- Add UART backend support for 1-Wire bus (`onewire_new_bus_uart`) alongside the existing RMT backend.
## 1.0.4
- Support `en_pull_up` config option in `onewire_bus_config_t`, which can enable the internal pull-up resistor on the GPIO pin used for the one-wire bus. This is useful when using a GPIO pin that does not have a pull-up resistor connected externally.
## 1.0.3
- Improve the driver to support esp-idf v6.0
## 1.0.2
- raise recovery time to support more sensor on longer wire (d0b2b52)
## 1.0.0
- Initial driver version, with the RMT driver as backend controller

View File

@@ -0,0 +1 @@
{"version":"1.0","algorithm":"sha256","created_at":"2026-04-02T08:31:31.921867+00:00","files":[{"path":".build-test-rules.yml","size":272,"hash":"d34faa08f404a108c17bcaf9709561dbc978c4f4fea0b912857758064430e069"},{"path":"CHANGELOG.md","size":591,"hash":"6be143366596176ad9f2d17932154117ef861918881e5abcfce678d55331e361"},{"path":"CMakeLists.txt","size":742,"hash":"8f26f2f876456b7533884d02b72dfee3481fe8617420c34cbfacf8c03dbc187a"},{"path":"LICENSE","size":11358,"hash":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"},{"path":"README.md","size":675,"hash":"ebc34c6d6383f828a433fced21289cc24a1248a0383c240aabb95912170c1cc6"},{"path":"idf_component.yml","size":386,"hash":"4054b22a198bae53f75aeacced4a6577dbd067df3d07567f0ff3930164d2bb10"},{"path":"include/onewire_bus.h","size":2993,"hash":"39804a54186091e6f6b17aba786d5e78b70e9ad8e9bbd79cf31989f282dada21"},{"path":"include/onewire_bus_impl_rmt.h","size":1323,"hash":"a8aeebf3cd5f23ddd00c76ec43dfd6f1a30eee819814fb0c0d50a1e292834511"},{"path":"include/onewire_bus_impl_uart.h","size":1253,"hash":"403a3cb182173148f7999dbfbd800b9baab80f51c3f00607b1ccfc18a91af108"},{"path":"include/onewire_cmd.h","size":356,"hash":"f0de787a3337b59e6a85c8a245ef5b56710830233ceeabf5fe59d1c0a0c50b38"},{"path":"include/onewire_crc.h","size":584,"hash":"9db2a7b437a6f72a0711698c9fad5ceca60458e828557cc38a3c8fc217fd5e06"},{"path":"include/onewire_device.h","size":1799,"hash":"f6b8202100de34d4b102953839e10df33b7850c35c599fb1d5bcf33ec1305773"},{"path":"include/onewire_types.h","size":1107,"hash":"bbfbaffac8df97fe53fcf42550f63dad9e3c29fac3fb26a03d735075eed85b31"},{"path":"interface/onewire_bus_interface.h","size":3196,"hash":"b03cea2218f99845b4486b7112760e08e4b7eb6b6dcc238f8d745afc593eae19"},{"path":"src/onewire_bus_api.c","size":1512,"hash":"894fdc649d552fe090c19bd3eb9e1433db71f95b8455b86faf5463636905ab3e"},{"path":"src/onewire_bus_impl_rmt.c","size":21406,"hash":"2e48338edb5e72065c239863811e387adc7164c2bb0f344bbca7a8e4ab07f624"},{"path":"src/onewire_bus_impl_uart.c","size":13129,"hash":"00f18bf1c3ea011c0952ca8dbf898a74094985932d7da2b4c8ff15ad99244576"},{"path":"src/onewire_crc.c","size":2193,"hash":"06a7ce5ffffbd1cbf4de35155cba2d5b903f718a0fbc3478265c09a4458a4ce0"},{"path":"src/onewire_device.c","size":4934,"hash":"3612afec5795eb9f47adef0b340c33c8584dde4d66d9581621ae203a6e9e031a"},{"path":"test_apps/CMakeLists.txt","size":135,"hash":"4035fd2167868739138e691d3080616b9d3b5c69adf567fbd33fb74b36a143ce"},{"path":"test_apps/pytest_onewire_bus.py","size":722,"hash":"c00451c79c16e3e3010045c21a21cb8c8e82217b898946061a4d133d096eca11"},{"path":"test_apps/sdkconfig.ci.rmt","size":75,"hash":"e8b859d77cdf1dc4125c5cdb4681dab1219db72c845f3f1b62f6d06397d804fa"},{"path":"test_apps/sdkconfig.ci.uart","size":75,"hash":"8413491b74b639e0a4e4d29968989d8434ed44bd225664352b631b7794c56320"},{"path":"test_apps/main/CMakeLists.txt","size":127,"hash":"2c724c0cf0fe496ed895d2360a281ad0c81b398c43cdfccb290c5b627c852f9a"},{"path":"test_apps/main/Kconfig.projbuild","size":1782,"hash":"70ccb326b523ed810f1597fe51c8b12a120156bd431552d0106c4f8e468c10c1"},{"path":"test_apps/main/idf_component.yml","size":83,"hash":"76ba47c24c863880ee8e6667ccde3ca1b68454993f31fd45755d5faa2ab83edc"},{"path":"test_apps/main/onewire_bus_test.c","size":2906,"hash":"f25f3a330bcc50fb30da60470c38401b88a3858415abe56668cf386e4f49a2d4"}]}

View File

@@ -0,0 +1,23 @@
set(srcs "src/onewire_bus_api.c"
"src/onewire_crc.c"
"src/onewire_device.c")
if(CONFIG_SOC_RMT_SUPPORTED)
list(APPEND srcs "src/onewire_bus_impl_rmt.c")
endif()
if(CONFIG_SOC_UART_SUPPORTED)
list(APPEND srcs "src/onewire_bus_impl_uart.c")
endif()
set(priv_requires)
# Starting from esp-idf v5.3, the peripheral drivers are in separate components
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
list(APPEND priv_requires "esp_driver_rmt" "esp_driver_uart" "esp_driver_gpio")
else()
list(APPEND priv_requires "driver")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "include" "interface"
PRIV_REQUIRES ${priv_requires})

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,13 @@
# 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.
The following low-level backends are currently supported:
- RMT backend (`onewire_new_bus_rmt`)
- UART backend (`onewire_new_bus_uart`)
## Appendix
* [DS18B20 device driver based on the 1-Wire Bus driver](https://components.espressif.com/components/espressif/ds18b20) and the [DS18B20 Example](https://github.com/espressif/esp-bsp/tree/master/components/ds18b20/examples/ds18b20-read)

View File

@@ -0,0 +1,10 @@
dependencies:
idf: '>=5.0'
description: Driver for Dallas 1-Wire bus
issues: https://github.com/espressif/idf-extra-components/issues
repository: git://github.com/espressif/idf-extra-components.git
repository_info:
commit_sha: bd6b21799cb9034e050a41b2c299b52a7e71be83
path: onewire_bus
url: https://github.com/espressif/idf-extra-components/tree/master/onewire_bus
version: 1.1.0

View File

@@ -0,0 +1,96 @@
/*
* 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 "soc/soc_caps.h"
#include "onewire_types.h"
#if SOC_RMT_SUPPORTED
#include "onewire_bus_impl_rmt.h"
#endif
#if SOC_UART_SUPPORTED
#include "onewire_bus_impl_uart.h"
#endif
#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 determines 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,42 @@
/*
* SPDX-FileCopyrightText: 2026 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 UART specific configuration
*/
typedef struct {
int uart_port_num; /*!< UART port number, e.g. UART_NUM_1 */
} onewire_bus_uart_config_t;
/**
* @brief Create 1-Wire bus with UART backend
*
* @note TX and RX will both be configured to bus_config->bus_gpio_num.
* And this GPIO will be configured as open-drain mode.
*
* @param[in] bus_config 1-Wire bus configuration
* @param[in] uart_config UART 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_uart(const onewire_bus_config_t *bus_config, const onewire_bus_uart_config_t *uart_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,43 @@
/*
* SPDX-FileCopyrightText: 2022-2025 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 */
struct onewire_bus_config_flags {
uint32_t en_pull_up: 1; /*!< Set true to enable internal pull-up resistor.
Please note the internal pull-up resistor cannot provide enough current for some devices,
so external pull-up resistor is still recommended. */
} flags; /*!< Configuration flags for the 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,521 @@
/*
* SPDX-FileCopyrightText: 2022-2025 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 "driver/gpio.h"
#include "esp_private/gpio.h"
#include "onewire_bus_impl_rmt.h"
#include "onewire_bus_interface.h"
#include "esp_idf_version.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 5 // 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 */
gpio_num_t data_gpio_num; /*!< GPIO number for 1-wire bus */
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");
bus_rmt->data_gpio_num = GPIO_NUM_NC;
// 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");
// create RX and TX channels and bind them to the same GPIO
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");
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,
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(6, 0, 0)
.flags.io_loop_back = true,
.flags.io_od_mode = true,
#endif
};
ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&onewire_tx_channel_cfg, &bus_rmt->tx_channel),
err, TAG, "create rmt tx channel failed");
bus_rmt->data_gpio_num = bus_config->bus_gpio_num;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
// enable open-drain mode for 1-wire bus
gpio_od_enable(bus_rmt->data_gpio_num);
#endif
if (bus_config->flags.en_pull_up) {
// enable internal pull-up resistor and disable pull-down resistor
gpio_set_pull_mode(bus_rmt->data_gpio_num, GPIO_PULLUP_ONLY);
} else {
// disable internal pull-up and pull-down resistors
gpio_set_pull_mode(bus_rmt->data_gpio_num, GPIO_FLOATING);
}
// 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);
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
if (bus_rmt->data_gpio_num != GPIO_NUM_NC) {
gpio_od_disable(bus_rmt->data_gpio_num);
}
#endif
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,298 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_check.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "onewire_bus_impl_uart.h"
#include "onewire_bus_interface.h"
#include "esp_idf_version.h"
static const char *TAG = "1-wire.uart";
#define ONEWIRE_UART_DEFAULT_TIMEOUT_MS 100
// refer to https://www.analog.com/en/resources/technical-articles/using-a-uart-to-implement-a-1wire-bus-master.html for more information
#define ONEWIRE_UART_BAUD_RESET 9600 // baud rate for reset pulse and presence detect
#define ONEWIRE_UART_BAUD_SLOT 115200 // baud rate for read and write operations
#define ONEWIRE_UART_RESET_TX 0xF0 // TX value for reset pulse and presence detect
#define ONEWIRE_UART_RESET_RX_NO_DEVICE 0xF0 // RX value when no device is present
#define ONEWIRE_UART_SLOT_TX_WRITE_1 0xFF // TX value for write 1
#define ONEWIRE_UART_SLOT_TX_WRITE_0 0x00 // TX value for write 0
#define ONEWIRE_UART_SLOT_TX_READ 0xFF // TX value for read
#define ONEWIRE_UART_SLOT_RX_READ_1 0xFF // RX value when read 1
typedef struct {
onewire_bus_t base; /*!< base class */
uart_port_t uart_port_num; /*!< UART port number */
gpio_num_t data_gpio_num; /*!< GPIO number for 1-wire bus */
uint32_t current_baud_rate; /*!< Note: the baud rate returned by uart_get_baudrate() could have a slight deviation from the user-configured baud rate.
That's why we store the configured baud rate here. */
SemaphoreHandle_t bus_mutex;
} onewire_bus_uart_obj_t;
static esp_err_t onewire_bus_uart_read_bit(onewire_bus_handle_t bus, uint8_t *rx_bit);
static esp_err_t onewire_bus_uart_write_bit(onewire_bus_handle_t bus, uint8_t tx_bit);
static esp_err_t onewire_bus_uart_read_bytes(onewire_bus_handle_t bus, uint8_t *rx_buf, size_t rx_buf_size);
static esp_err_t onewire_bus_uart_write_bytes(onewire_bus_handle_t bus, const uint8_t *tx_data, uint8_t tx_data_size);
static esp_err_t onewire_bus_uart_reset(onewire_bus_handle_t bus);
static esp_err_t onewire_bus_uart_del(onewire_bus_handle_t bus);
static esp_err_t onewire_bus_uart_destroy(onewire_bus_uart_obj_t *bus_uart);
static esp_err_t onewire_bus_uart_set_baud_rate(onewire_bus_uart_obj_t *bus_uart, uint32_t baud_rate);
static esp_err_t onewire_bus_uart_exchange_byte(onewire_bus_uart_obj_t *bus_uart, uint8_t tx_data, uint8_t *rx_data);
static esp_err_t onewire_bus_uart_write_bit_nolock(onewire_bus_uart_obj_t *bus_uart, uint8_t tx_bit);
static esp_err_t onewire_bus_uart_read_bit_nolock(onewire_bus_uart_obj_t *bus_uart, uint8_t *rx_bit);
esp_err_t onewire_new_bus_uart(const onewire_bus_config_t *bus_config, const onewire_bus_uart_config_t *uart_config, onewire_bus_handle_t *ret_bus)
{
esp_err_t ret = ESP_OK;
onewire_bus_uart_obj_t *bus_uart = NULL;
ESP_RETURN_ON_FALSE(bus_config && uart_config && ret_bus, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(bus_config->bus_gpio_num), ESP_ERR_INVALID_ARG, TAG, "invalid GPIO number");
bus_uart = calloc(1, sizeof(onewire_bus_uart_obj_t));
ESP_RETURN_ON_FALSE(bus_uart, ESP_ERR_NO_MEM, TAG, "no mem for onewire_bus_uart_obj_t");
bus_uart->uart_port_num = UART_NUM_MAX;
bus_uart->data_gpio_num = GPIO_NUM_NC;
const gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << bus_config->bus_gpio_num),
.mode = GPIO_MODE_INPUT_OUTPUT_OD,
.pull_up_en = bus_config->flags.en_pull_up ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "gpio config failed");
bus_uart->data_gpio_num = bus_config->bus_gpio_num;
// Simulate a 1-Wire bus using UART 8N1 mode
// refer to https://www.analog.com/en/resources/technical-articles/using-a-uart-to-implement-a-1wire-bus-master.html for more information
const uart_config_t uart_cfg = {
.baud_rate = ONEWIRE_UART_BAUD_RESET,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
ESP_GOTO_ON_ERROR(uart_param_config(uart_config->uart_port_num, &uart_cfg), err, TAG, "uart param config failed");
bus_uart->current_baud_rate = uart_cfg.baud_rate;
ESP_GOTO_ON_ERROR(uart_set_pin(uart_config->uart_port_num, bus_config->bus_gpio_num, bus_config->bus_gpio_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE),
err, TAG, "uart set pin failed");
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0)
// Set the RX buffer to minimum size because we only need to receive 1 byte at a time
// Disable the TX buffer because we only need to send 1 byte at a time
ESP_GOTO_ON_ERROR(uart_driver_install(uart_config->uart_port_num, UART_HW_FIFO_LEN(uart_config->uart_port_num) + 1, 0, 0, NULL, 0),
err, TAG, "uart driver install failed");
#else
ESP_GOTO_ON_ERROR(uart_driver_install(uart_config->uart_port_num, UART_FIFO_LEN + 1, 0, 0, NULL, 0),
err, TAG, "uart driver install failed");
#endif
bus_uart->uart_port_num = uart_config->uart_port_num;
// Configuration optimized for this scenario
// We only need to receive 1 byte at a time, so set the rx full threshold to 1 for faster response
// Normally, the RX timeout interrupt is not expected to trigger. Setting the timeout to 1 here is simply to ensure a fast response
ESP_GOTO_ON_ERROR(uart_set_rx_full_threshold(uart_config->uart_port_num, 1), err, TAG, "uart set rx full threshold failed");
ESP_GOTO_ON_ERROR(uart_set_rx_timeout(uart_config->uart_port_num, 1), err, TAG, "uart set rx timeout failed");
bus_uart->bus_mutex = xSemaphoreCreateMutex();
ESP_GOTO_ON_FALSE(bus_uart->bus_mutex, ESP_ERR_NO_MEM, err, TAG, "bus mutex creation failed");
bus_uart->base.del = onewire_bus_uart_del;
bus_uart->base.reset = onewire_bus_uart_reset;
bus_uart->base.write_bit = onewire_bus_uart_write_bit;
bus_uart->base.write_bytes = onewire_bus_uart_write_bytes;
bus_uart->base.read_bit = onewire_bus_uart_read_bit;
bus_uart->base.read_bytes = onewire_bus_uart_read_bytes;
*ret_bus = &bus_uart->base;
return ret;
err:
if (bus_uart) {
onewire_bus_uart_destroy(bus_uart);
}
return ret;
}
static esp_err_t onewire_bus_uart_destroy(onewire_bus_uart_obj_t *bus_uart)
{
if (bus_uart->bus_mutex) {
vSemaphoreDelete(bus_uart->bus_mutex);
}
if (bus_uart->uart_port_num != UART_NUM_MAX) {
uart_driver_delete(bus_uart->uart_port_num);
}
if (bus_uart->data_gpio_num != GPIO_NUM_NC) {
gpio_reset_pin(bus_uart->data_gpio_num);
}
free(bus_uart);
return ESP_OK;
}
static esp_err_t onewire_bus_uart_set_baud_rate(onewire_bus_uart_obj_t *bus_uart, uint32_t baud_rate)
{
if (bus_uart->current_baud_rate == baud_rate) {
return ESP_OK;
}
esp_err_t ret = uart_set_baudrate(bus_uart->uart_port_num, baud_rate);
if (ret != ESP_OK) {
return ret;
}
bus_uart->current_baud_rate = baud_rate;
return ESP_OK;
}
/**
* @brief Send and receive one byte over the UART bus.
*
* @note This function is used for:
* - reset pulse and presence detect
* - write or read one bit on the 1-wire bus
*/
static esp_err_t onewire_bus_uart_exchange_byte(onewire_bus_uart_obj_t *bus_uart, uint8_t tx_data, uint8_t *rx_data)
{
esp_err_t ret = uart_flush_input(bus_uart->uart_port_num);
if (ret != ESP_OK) {
return ret;
}
if (uart_tx_chars(bus_uart->uart_port_num, (const char *)&tx_data, 1) != 1) {
return ESP_FAIL;
}
if (uart_read_bytes(bus_uart->uart_port_num, rx_data, 1, pdMS_TO_TICKS(ONEWIRE_UART_DEFAULT_TIMEOUT_MS)) != 1) {
return ESP_ERR_TIMEOUT;
}
return ESP_OK;
}
static esp_err_t onewire_bus_uart_write_bit_nolock(onewire_bus_uart_obj_t *bus_uart, uint8_t tx_bit)
{
uint8_t rx_data = 0;
const uint8_t tx_data = tx_bit ? ONEWIRE_UART_SLOT_TX_WRITE_1 : ONEWIRE_UART_SLOT_TX_WRITE_0;
esp_err_t ret = onewire_bus_uart_exchange_byte(bus_uart, tx_data, &rx_data);
if (ret != ESP_OK) {
return ret;
}
return (rx_data == tx_data) ? ESP_OK : ESP_ERR_INVALID_STATE; // Check if the sent data is corrupted
}
static esp_err_t onewire_bus_uart_read_bit_nolock(onewire_bus_uart_obj_t *bus_uart, uint8_t *rx_bit)
{
uint8_t rx_data = 0;
esp_err_t ret = onewire_bus_uart_exchange_byte(bus_uart, ONEWIRE_UART_SLOT_TX_READ, &rx_data);
if (ret != ESP_OK) {
return ret;
}
*rx_bit = (rx_data == ONEWIRE_UART_SLOT_RX_READ_1) ? 1 : 0;
return ESP_OK;
}
////////////////////////////// implementation of onewire_bus_t functions //////////////////////////////
static esp_err_t onewire_bus_uart_del(onewire_bus_handle_t bus)
{
onewire_bus_uart_obj_t *bus_uart = __containerof(bus, onewire_bus_uart_obj_t, base);
return onewire_bus_uart_destroy(bus_uart);
}
static esp_err_t onewire_bus_uart_reset(onewire_bus_handle_t bus)
{
onewire_bus_uart_obj_t *bus_uart = __containerof(bus, onewire_bus_uart_obj_t, base);
esp_err_t ret = ESP_OK;
uint8_t rx_data = 0;
xSemaphoreTake(bus_uart->bus_mutex, portMAX_DELAY);
ESP_GOTO_ON_ERROR(onewire_bus_uart_set_baud_rate(bus_uart, ONEWIRE_UART_BAUD_RESET), err, TAG, "set reset baudrate failed");
ESP_GOTO_ON_ERROR(onewire_bus_uart_exchange_byte(bus_uart, ONEWIRE_UART_RESET_TX, &rx_data), err, TAG, "create reset pulse failed");
ESP_GOTO_ON_FALSE(rx_data != ONEWIRE_UART_RESET_RX_NO_DEVICE, ESP_ERR_NOT_FOUND, err, TAG, "no 1-wire device found");
err:
xSemaphoreGive(bus_uart->bus_mutex);
return ret;
}
static esp_err_t onewire_bus_uart_write_bytes(onewire_bus_handle_t bus, const uint8_t *tx_data, uint8_t tx_data_size)
{
onewire_bus_uart_obj_t *bus_uart = __containerof(bus, onewire_bus_uart_obj_t, base);
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(tx_data && tx_data_size, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
xSemaphoreTake(bus_uart->bus_mutex, portMAX_DELAY);
ESP_GOTO_ON_ERROR(onewire_bus_uart_set_baud_rate(bus_uart, ONEWIRE_UART_BAUD_SLOT), err, TAG, "set slot baudrate failed");
for (uint8_t i = 0; i < tx_data_size; i++) {
uint8_t current = tx_data[i];
for (int bit = 0; bit < 8; bit++) {
ESP_GOTO_ON_ERROR(onewire_bus_uart_write_bit_nolock(bus_uart, current & 0x01), err, TAG, "write bit failed");
current >>= 1;
}
}
err:
xSemaphoreGive(bus_uart->bus_mutex);
return ret;
}
static esp_err_t onewire_bus_uart_read_bytes(onewire_bus_handle_t bus, uint8_t *rx_buf, size_t rx_buf_size)
{
onewire_bus_uart_obj_t *bus_uart = __containerof(bus, onewire_bus_uart_obj_t, base);
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(rx_buf && rx_buf_size, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
memset(rx_buf, 0, rx_buf_size);
xSemaphoreTake(bus_uart->bus_mutex, portMAX_DELAY);
ESP_GOTO_ON_ERROR(onewire_bus_uart_set_baud_rate(bus_uart, ONEWIRE_UART_BAUD_SLOT), err, TAG, "set slot baudrate failed");
for (size_t i = 0; i < rx_buf_size; i++) {
uint8_t current = 0;
for (int bit = 0; bit < 8; bit++) {
uint8_t rx_bit = 0;
ESP_GOTO_ON_ERROR(onewire_bus_uart_read_bit_nolock(bus_uart, &rx_bit), err, TAG, "read bit failed");
if (rx_bit) {
current |= (1 << bit);
}
}
rx_buf[i] = current;
}
err:
xSemaphoreGive(bus_uart->bus_mutex);
return ret;
}
static esp_err_t onewire_bus_uart_write_bit(onewire_bus_handle_t bus, uint8_t tx_bit)
{
onewire_bus_uart_obj_t *bus_uart = __containerof(bus, onewire_bus_uart_obj_t, base);
esp_err_t ret = ESP_OK;
xSemaphoreTake(bus_uart->bus_mutex, portMAX_DELAY);
ESP_GOTO_ON_ERROR(onewire_bus_uart_set_baud_rate(bus_uart, ONEWIRE_UART_BAUD_SLOT), err, TAG, "set slot baudrate failed");
ESP_GOTO_ON_ERROR(onewire_bus_uart_write_bit_nolock(bus_uart, tx_bit), err, TAG, "write bit failed");
err:
xSemaphoreGive(bus_uart->bus_mutex);
return ret;
}
static esp_err_t onewire_bus_uart_read_bit(onewire_bus_handle_t bus, uint8_t *rx_bit)
{
onewire_bus_uart_obj_t *bus_uart = __containerof(bus, onewire_bus_uart_obj_t, base);
ESP_RETURN_ON_FALSE(rx_bit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
esp_err_t ret = ESP_OK;
xSemaphoreTake(bus_uart->bus_mutex, portMAX_DELAY);
ESP_GOTO_ON_ERROR(onewire_bus_uart_set_baud_rate(bus_uart, ONEWIRE_UART_BAUD_SLOT), err, TAG, "set slot baudrate failed");
ESP_GOTO_ON_ERROR(onewire_bus_uart_read_bit_nolock(bus_uart, rx_bit), err, TAG, "read bit failed");
err:
xSemaphoreGive(bus_uart->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 corresponding 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;
}

View File

@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)
project(onewire_bus_test)

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "onewire_bus_test.c"
INCLUDE_DIRS "."
PRIV_REQUIRES unity)

View File

@@ -0,0 +1,53 @@
menu "Example Configuration"
choice EXAMPLE_ONEWIRE_BACKEND
prompt "1-Wire backend"
default EXAMPLE_ONEWIRE_BACKEND_RMT if SOC_RMT_SUPPORTED
default EXAMPLE_ONEWIRE_BACKEND_UART if SOC_UART_SUPPORTED
help
Select which low-level backend the test app uses.
config EXAMPLE_ONEWIRE_BACKEND_RMT
bool "RMT backend"
depends on SOC_RMT_SUPPORTED
config EXAMPLE_ONEWIRE_BACKEND_UART
bool "UART backend"
depends on SOC_UART_SUPPORTED
endchoice
config EXAMPLE_ONEWIRE_UART_PORT_NUM
int "UART port number"
depends on EXAMPLE_ONEWIRE_BACKEND_UART
default 1
range 0 2
help
UART port used by UART backend.
Valid UART port numbers differ across targets.
Note: UART0 is typically used by the console output.
config EXAMPLE_ONEWIRE_BUS_GPIO
int "1-Wire data GPIO"
default 0
range 0 63
help
GPIO number used for the 1-Wire data line.
Valid GPIO numbers depend on the selected target; choose a
data-capable GPIO within this range for your chip.
config EXAMPLE_ONEWIRE_ENABLE_INTERNAL_PULLUP
bool "Enable internal pull-up"
default y
help
Enable internal pull-up resistor on the GPIO pin used for the 1-Wire data line.
This is useful when no external pull-up resistor is present.
config EXAMPLE_ONEWIRE_MAX_DEVICES
int "Maximum devices to search"
default 2
range 1 64
help
Maximum number of devices to search on the 1-Wire bus.
This test app performs SEARCH ROM to collect device addresses (64-bit ROM IDs).
endmenu

View File

@@ -0,0 +1,4 @@
dependencies:
espressif/onewire_bus:
version: "*"
override_path: "../.."

View File

@@ -0,0 +1,80 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include "esp_log.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "onewire_bus.h"
#include "onewire_device.h"
static const char *TAG = "test-app";
#if CONFIG_EXAMPLE_ONEWIRE_ENABLE_INTERNAL_PULLUP
#define EXAMPLE_ONEWIRE_ENABLE_INTERNAL_PULLUP 1
#else
#define EXAMPLE_ONEWIRE_ENABLE_INTERNAL_PULLUP 0
#endif
#if CONFIG_EXAMPLE_ONEWIRE_BACKEND_UART
#define EXAMPLE_ONEWIRE_UART_PORT_NUM CONFIG_EXAMPLE_ONEWIRE_UART_PORT_NUM
#endif
#define EXAMPLE_ONEWIRE_BUS_GPIO CONFIG_EXAMPLE_ONEWIRE_BUS_GPIO
#define EXAMPLE_ONEWIRE_MAX_DEVICES CONFIG_EXAMPLE_ONEWIRE_MAX_DEVICES
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,
.flags = {
.en_pull_up = EXAMPLE_ONEWIRE_ENABLE_INTERNAL_PULLUP,
}
};
#if CONFIG_EXAMPLE_ONEWIRE_BACKEND_RMT
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 by RMT backend", EXAMPLE_ONEWIRE_BUS_GPIO);
#elif CONFIG_EXAMPLE_ONEWIRE_BACKEND_UART
onewire_bus_uart_config_t uart_config = {
.uart_port_num = EXAMPLE_ONEWIRE_UART_PORT_NUM,
};
ESP_ERROR_CHECK(onewire_new_bus_uart(&bus_config, &uart_config, &bus));
ESP_LOGI(TAG, "1-Wire bus installed on GPIO%d by UART backend (UART%d)",
EXAMPLE_ONEWIRE_BUS_GPIO, EXAMPLE_ONEWIRE_UART_PORT_NUM);
#else
#error "No 1-Wire backend selected in menuconfig"
#endif
int onewire_device_found = 0;
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);
// found a new device
if (search_result == ESP_OK) {
ESP_LOGI(TAG, "Found a new device, address: %016llX", next_onewire_device.address);
onewire_device_found++;
if (onewire_device_found >= EXAMPLE_ONEWIRE_MAX_DEVICES) {
ESP_LOGI(TAG, "Max device number reached, stop searching...");
break;
}
}
} while (search_result != ESP_ERR_NOT_FOUND);
ESP_ERROR_CHECK(onewire_del_device_iter(iter));
ESP_LOGI(TAG, "Searching done, %d device(s) found", onewire_device_found);
// delete the bus
ESP_LOGI(TAG, "Deleting bus...");
ESP_ERROR_CHECK(onewire_bus_del(bus));
}

View File

@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.generic
@pytest.mark.parametrize('config', ['rmt', 'uart'], indirect=True)
def test_onewire_bus(dut: Dut, config: str) -> None:
if config == 'rmt':
dut.expect_exact('test-app: 1-Wire bus installed on GPIO0 by RMT backend')
elif config == 'uart':
dut.expect_exact('test-app: 1-Wire bus installed on GPIO0 by UART backend (UART1)')
else:
raise ValueError(f'Unknown test config: {config}')
dut.expect_exact('test-app: Device iterator created, start searching')
dut.expect_exact('test-app: Searching done')