diff --git a/.vscode/settings.json b/.vscode/settings.json index f1ff58a..7fbbf25 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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": [ diff --git a/components/espressif_ds18b20/.DS_Store b/components/espressif_ds18b20/.DS_Store new file mode 100644 index 0000000..cd3521a Binary files /dev/null and b/components/espressif_ds18b20/.DS_Store differ diff --git a/components/espressif_ds18b20/CHANGELOG.md b/components/espressif_ds18b20/CHANGELOG.md new file mode 100644 index 0000000..d8c5ad7 --- /dev/null +++ b/components/espressif_ds18b20/CHANGELOG.md @@ -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. diff --git a/components/espressif_ds18b20/CMakeLists.txt b/components/espressif_ds18b20/CMakeLists.txt new file mode 100644 index 0000000..7793bcd --- /dev/null +++ b/components/espressif_ds18b20/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "src/ds18b20.c" + INCLUDE_DIRS "include") diff --git a/components/espressif_ds18b20/LICENSE b/components/espressif_ds18b20/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/components/espressif_ds18b20/LICENSE @@ -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. diff --git a/components/espressif_ds18b20/README.md b/components/espressif_ds18b20/README.md new file mode 100644 index 0000000..f1d55f9 --- /dev/null +++ b/components/espressif_ds18b20/README.md @@ -0,0 +1,63 @@ +# DS18B20 Device Driver + +[](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) diff --git a/components/espressif_ds18b20/idf_component.yml b/components/espressif_ds18b20/idf_component.yml new file mode 100644 index 0000000..4b3714f --- /dev/null +++ b/components/espressif_ds18b20/idf_component.yml @@ -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 diff --git a/components/espressif_ds18b20/include/ds18b20.h b/components/espressif_ds18b20/include/ds18b20.h new file mode 100644 index 0000000..4e1113a --- /dev/null +++ b/components/espressif_ds18b20/include/ds18b20.h @@ -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 diff --git a/components/espressif_ds18b20/include/ds18b20_types.h b/components/espressif_ds18b20/include/ds18b20_types.h new file mode 100644 index 0000000..478089c --- /dev/null +++ b/components/espressif_ds18b20/include/ds18b20_types.h @@ -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 diff --git a/components/espressif_ds18b20/src/ds18b20.c b/components/espressif_ds18b20/src/ds18b20.c new file mode 100644 index 0000000..012a84f --- /dev/null +++ b/components/espressif_ds18b20/src/ds18b20.c @@ -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; +} diff --git a/components/espressif_onewire_bus/.DS_Store b/components/espressif_onewire_bus/.DS_Store new file mode 100644 index 0000000..314644f Binary files /dev/null and b/components/espressif_onewire_bus/.DS_Store differ diff --git a/components/espressif_onewire_bus/CHANGELOG.md b/components/espressif_onewire_bus/CHANGELOG.md new file mode 100644 index 0000000..4e7a21c --- /dev/null +++ b/components/espressif_onewire_bus/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial driver version, with the RMT driver as backend controller diff --git a/components/espressif_onewire_bus/CMakeLists.txt b/components/espressif_onewire_bus/CMakeLists.txt new file mode 100644 index 0000000..b1b7296 --- /dev/null +++ b/components/espressif_onewire_bus/CMakeLists.txt @@ -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) diff --git a/components/espressif_onewire_bus/LICENSE b/components/espressif_onewire_bus/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/components/espressif_onewire_bus/LICENSE @@ -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. diff --git a/components/espressif_onewire_bus/README.md b/components/espressif_onewire_bus/README.md new file mode 100644 index 0000000..afc865e --- /dev/null +++ b/components/espressif_onewire_bus/README.md @@ -0,0 +1,5 @@ +# Dallas 1-Wire Bus Driver + +[](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. diff --git a/components/espressif_onewire_bus/idf_component.yml b/components/espressif_onewire_bus/idf_component.yml new file mode 100644 index 0000000..35c1ae6 --- /dev/null +++ b/components/espressif_onewire_bus/idf_component.yml @@ -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 diff --git a/components/espressif_onewire_bus/include/onewire_bus.h b/components/espressif_onewire_bus/include/onewire_bus.h new file mode 100644 index 0000000..51206da --- /dev/null +++ b/components/espressif_onewire_bus/include/onewire_bus.h @@ -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 diff --git a/components/espressif_onewire_bus/include/onewire_bus_impl_rmt.h b/components/espressif_onewire_bus/include/onewire_bus_impl_rmt.h new file mode 100644 index 0000000..6aaee46 --- /dev/null +++ b/components/espressif_onewire_bus/include/onewire_bus_impl_rmt.h @@ -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 diff --git a/components/espressif_onewire_bus/include/onewire_cmd.h b/components/espressif_onewire_bus/include/onewire_cmd.h new file mode 100644 index 0000000..5a091b5 --- /dev/null +++ b/components/espressif_onewire_bus/include/onewire_cmd.h @@ -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 diff --git a/components/espressif_onewire_bus/include/onewire_crc.h b/components/espressif_onewire_bus/include/onewire_crc.h new file mode 100644 index 0000000..c754d1f --- /dev/null +++ b/components/espressif_onewire_bus/include/onewire_crc.h @@ -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 diff --git a/components/espressif_onewire_bus/include/onewire_device.h b/components/espressif_onewire_bus/include/onewire_device.h new file mode 100644 index 0000000..fee0c1f --- /dev/null +++ b/components/espressif_onewire_bus/include/onewire_device.h @@ -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 diff --git a/components/espressif_onewire_bus/include/onewire_types.h b/components/espressif_onewire_bus/include/onewire_types.h new file mode 100644 index 0000000..6d7b120 --- /dev/null +++ b/components/espressif_onewire_bus/include/onewire_types.h @@ -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 diff --git a/components/espressif_onewire_bus/interface/onewire_bus_interface.h b/components/espressif_onewire_bus/interface/onewire_bus_interface.h new file mode 100644 index 0000000..9920c4e --- /dev/null +++ b/components/espressif_onewire_bus/interface/onewire_bus_interface.h @@ -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 diff --git a/components/espressif_onewire_bus/src/onewire_bus_api.c b/components/espressif_onewire_bus/src/onewire_bus_api.c new file mode 100644 index 0000000..3cded8f --- /dev/null +++ b/components/espressif_onewire_bus/src/onewire_bus_api.c @@ -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); +} diff --git a/components/espressif_onewire_bus/src/onewire_bus_impl_rmt.c b/components/espressif_onewire_bus/src/onewire_bus_impl_rmt.c new file mode 100644 index 0000000..78243e6 --- /dev/null +++ b/components/espressif_onewire_bus/src/onewire_bus_impl_rmt.c @@ -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(©_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; +} diff --git a/components/espressif_onewire_bus/src/onewire_crc.c b/components/espressif_onewire_bus/src/onewire_crc.c new file mode 100644 index 0000000..81595b1 --- /dev/null +++ b/components/espressif_onewire_bus/src/onewire_crc.c @@ -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 diff --git a/components/espressif_onewire_bus/src/onewire_device.c b/components/espressif_onewire_bus/src/onewire_device.c new file mode 100644 index 0000000..82a1e32 --- /dev/null +++ b/components/espressif_onewire_bus/src/onewire_device.c @@ -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; +} diff --git a/components/onewire/.DS_Store b/components/onewire/.DS_Store new file mode 100644 index 0000000..87de984 Binary files /dev/null and b/components/onewire/.DS_Store differ diff --git a/components/onewire/CMakeLists.txt b/components/onewire/CMakeLists.txt new file mode 100644 index 0000000..8e81547 --- /dev/null +++ b/components/onewire/CMakeLists.txt @@ -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) diff --git a/components/onewire/README.md b/components/onewire/README.md new file mode 100644 index 0000000..1cb065e --- /dev/null +++ b/components/onewire/README.md @@ -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. diff --git a/components/onewire/main/CMakeLists.txt b/components/onewire/main/CMakeLists.txt new file mode 100644 index 0000000..cc6bae1 --- /dev/null +++ b/components/onewire/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "onewire_example_main.c" + INCLUDE_DIRS ".") diff --git a/components/onewire/main/idf_component.yml b/components/onewire/main/idf_component.yml new file mode 100644 index 0000000..1ca3e8b --- /dev/null +++ b/components/onewire/main/idf_component.yml @@ -0,0 +1,2 @@ +dependencies: + ds18b20: "^0.1.0" diff --git a/components/onewire/main/onewire_example_main.c b/components/onewire/main/onewire_example_main.c new file mode 100644 index 0000000..d3c3b41 --- /dev/null +++ b/components/onewire/main/onewire_example_main.c @@ -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); + } + } +} diff --git a/components/onewire/pytest_onewire.py b/components/onewire/pytest_onewire.py new file mode 100644 index 0000000..fc7c045 --- /dev/null +++ b/components/onewire/pytest_onewire.py @@ -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') diff --git a/components/zh_onewire/zh_onewire.c b/components/zh_onewire/zh_onewire.c index 4d94a3f..ab8c226 100644 --- a/components/zh_onewire/zh_onewire.c +++ b/components/zh_onewire/zh_onewire.c @@ -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);