Merge branch 'example/rmt_ds18b20' into 'master'

example: simulate 1-wire protocol with RMT driver (take DS18B20 as example)

Closes IDF-1457

See merge request espressif/esp-idf!18857
This commit is contained in:
morris 2022-07-20 23:16:40 +08:00
commit 2e47a422e3
16 changed files with 1181 additions and 4 deletions

View File

@ -38,7 +38,8 @@ typedef struct {
struct {
uint32_t invert_out: 1; /*!< Whether to invert the RMT channel signal before output to GPIO pad */
uint32_t with_dma: 1; /*!< If set, the driver will allocate an RMT channel with DMA capability */
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
uint32_t io_loop_back: 1; /*!< The signal output from the GPIO will be fed to the input path as well */
uint32_t io_od_mode: 1; /*!< Configure the GPIO as open-drain mode */
} flags; /*!< TX channel config flags */
} rmt_tx_channel_config_t;

View File

@ -276,8 +276,8 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
tx_channel->base.gpio_num = config->gpio_num;
gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE,
// also enable the input path is `io_loop_back` is on, this is useful for debug
.mode = GPIO_MODE_OUTPUT | (config->flags.io_loop_back ? GPIO_MODE_INPUT : 0),
// also enable the input path is `io_loop_back` is on, this is useful for bi-directional buses
.mode = (config->flags.io_od_mode ? GPIO_MODE_OUTPUT_OD : GPIO_MODE_OUTPUT) | (config->flags.io_loop_back ? GPIO_MODE_INPUT : 0),
.pull_down_en = false,
.pull_up_en = true,
.pin_bit_mask = 1ULL << config->gpio_num,

View File

@ -82,7 +82,8 @@ To install an RMT TX channel, there's a configuration structure that needs to be
- :cpp:member:`rmt_tx_channel_config_t::trans_queue_depth` sets the depth of internal transaction queue, the deeper the queue, the more transactions can be prepared in the backlog.
- :cpp:member:`rmt_tx_channel_config_t::invert_out` is used to decide whether to invert the RMT signal before sending it to the GPIO pad.
- :cpp:member:`rmt_tx_channel_config_t::with_dma` is used to indicate if the channel needs a DMA backend. A channel with DMA attached can offload the CPU by a lot. However, DMA backend is not available on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before you enable this option. Or you might encounter :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
- :cpp:member:`rmt_tx_channel_config_t::io_loop_back` is for debugging purposes only. It enables both the GPIO's input and output ability through the GPIO matrix peripheral. Meanwhile, if both TX and RX channels are bound to the same GPIO, then monitoring of the data transmission line can be realized.
- :cpp:member:`rmt_tx_channel_config_t::io_loop_back` enables both the GPIO's input and output ability through the GPIO matrix peripheral. Meanwhile, if both TX and RX channels are bound to the same GPIO, then monitoring of the data transmission line can be realized.
- :cpp:member:`rmt_tx_channel_config_t::io_od_mode` configures the GPIO as open-drain mode. It is useful for simulating bi-directional buses, sucn as 1-wire bus, combined with :cpp:member:`rmt_tx_channel_config_t::io_loop_back`.
Once the :cpp:type:`rmt_tx_channel_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`rmt_new_tx_channel` to allocate and initialize a TX channel. This function will return an RMT channel handle if it runs correctly. Specifically, when there are no more free channels in the RMT resource pool, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. If some feature (e.g. DMA backend) is not supported by hardware, it will return :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
@ -517,6 +518,7 @@ Application Examples
* RMT transactions in queue: :example:`peripherals/rmt/musical_buzzer`
* RMT based stepper motor with S-curve algorithm: : :example:`peripherals/rmt/stepper_motor`
* RMT infinite loop for driving DShot ESC: :example:`peripherals/rmt/dshot_esc`
* RMT simulate 1-wire protocol (take DS18B20 as example): :example:`peripherals/rmt/onewire_ds18b20`
API Reference
-------------

View File

@ -84,6 +84,10 @@ examples/peripherals/rmt/musical_buzzer:
enable:
- if: SOC_RMT_SUPPORT_TX_LOOP_COUNT == 1
examples/peripherals/rmt/onewire_ds18b20:
disable:
- if: SOC_RMT_SUPPORTED != 1
examples/peripherals/rmt/stepper_motor:
enable:
- if: SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP == 1

View File

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

View File

@ -0,0 +1,90 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- |
# RMT Transmit & Receive Example -- 1-Wire bus
(See the README.md file in the upper level 'examples' directory for more information about examples.)
RMT peripheral has both transmit and receive channels. Connecting one transmit channel and one receive channel to the same GPIO and put the GPIO in open-drain mode can simulate bi-directional single wire protocols, such as [1-Wire protocol](https://www.maximintegrated.com/en/design/technical-documents/tutorials/1/1796.html).
This example demonstrates how to use RMT to simulate 1-Wire bus and read temperatrue from [DS18B20](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf).
## How to Use Example
### Hardware Required
* A development board with any supported Espressif SOC chip (see `Supported Targets` table above)
* Several DS18B20 temperature sensors and a 4.7kohm pullup resistor
Connection :
```
┌──────────────────────────┐
│ 3.3V├───────┬─────────────┬──────────────────────┐
│ │ ┌┴┐ │VDD │VDD
│ ESP32 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_GPIO_PIN` defined in [onewire_ds18b20_example_main.c](main/onewire_ds18b20_example_main.c).
*Note*: Parasite power mode is not supported currently by this example, 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
If there are some DS18B20s on the bus:
```
I (327) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (338) gpio: GPIO[5]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (348) onewire_rmt: RMT Tx channel created for 1-wire bus
I (358) gpio: GPIO[5]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0
I (358) onewire_rmt: RMT Rx channel created for 1-wire bus
I (368) example: 1-wire bus installed
I (418) example: found device with rom id 28FF30BE21170317
I (458) example: found device with rom id 28FF297E211703A1
I (498) example: found device with rom id 28FF6F7921170352
I (508) example: 3 devices found on 1-wire bus
I (2518) example: temperature of device 28FF30BE21170317: 27.00C
I (2528) example: temperature of device 28FF297E211703A1: 26.81C
I (2538) example: temperature of device 28FF6F7921170352: 26.50C
I (3548) example: temperature of device 28FF30BE21170317: 26.94C
I (3558) example: temperature of device 28FF297E211703A1: 26.75C
I (3568) example: temperature of device 28FF6F7921170352: 26.44C
```
If there is no DS18B20 on the bus:
```
I (327) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (337) gpio: GPIO[5]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (347) onewire_rmt: RMT Tx channel created for 1-wire bus
I (357) gpio: GPIO[5]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0
I (357) onewire_rmt: RMT Rx channel created for 1-wire bus
I (367) example: 1-wire bus installed
E (377) onewire_rmt: no device present on 1-wire bus
I (377) example: 0 device found on 1-wire bus
I (387) gpio: GPIO[5]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (397) gpio: GPIO[5]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (397) example: 1-wire bus deleted
```
## Troubleshooting
For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "onewire_bus_rmt.c" "onewire_bus.c"
INCLUDE_DIRS "."
PRIV_REQUIRES driver)

View File

@ -0,0 +1,158 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_check.h"
#include "esp_log.h"
#include "onewire_bus.h"
static const char *TAG = "onewire";
struct onewire_rom_search_context_t {
onewire_bus_handle_t bus_handle;
uint8_t last_device_flag;
uint16_t last_discrepancy;
uint8_t rom_number[8];
};
// Algorithm inspired by https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/187.html
static const uint8_t dscrc_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_check_crc8(uint8_t *input, size_t input_size)
{
uint8_t crc8 = 0;
for (size_t i = 0; i < input_size; i ++) {
crc8 = dscrc_table[crc8 ^ input[i]];
}
return crc8;
}
esp_err_t onewire_rom_search_context_create(onewire_bus_handle_t handle, onewire_rom_search_context_handler_t *context_out)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
ESP_RETURN_ON_FALSE(context_out, ESP_ERR_INVALID_ARG, TAG, "invalid context handler pointer");
struct onewire_rom_search_context_t *context = calloc(1, sizeof(struct onewire_rom_search_context_t));
if (!context) {
return ESP_ERR_NO_MEM;
}
context->bus_handle = handle;
*context_out = context;
return ESP_OK;
}
esp_err_t onewire_rom_search_context_delete(onewire_rom_search_context_handler_t context)
{
ESP_RETURN_ON_FALSE(context, ESP_ERR_INVALID_ARG, TAG, "invalid context handler pointer");
free(context);
return ESP_OK;
}
esp_err_t onewire_rom_search(onewire_rom_search_context_handler_t context)
{
ESP_RETURN_ON_FALSE(context, ESP_ERR_INVALID_ARG, TAG, "invalid context handler pointer");
uint8_t last_zero = 0;
if (!context->last_device_flag) {
if (onewire_bus_reset(context->bus_handle) != ESP_OK) { // no device present
return ESP_ERR_NOT_FOUND;
}
// send rom search command and start search algorithm
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(context->bus_handle, (uint8_t[]){ONEWIRE_CMD_SEARCH_ROM}, 1),
TAG, "error while sending search rom command");
for (uint16_t rom_bit_index = 0; rom_bit_index < 64; 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, rom_bit_complement;
ESP_RETURN_ON_ERROR(onewire_bus_read_bit(context->bus_handle, &rom_bit), TAG, "error while reading rom bit"); // write 1 bit to read from the bus
ESP_RETURN_ON_ERROR(onewire_bus_read_bit(context->bus_handle, &rom_bit_complement),
TAG, "error while reading rom bit"); // read a bit and its complement
uint8_t search_direction;
if (rom_bit && rom_bit_complement) { // No devices participating in search.
ESP_LOGE(TAG, "no devices participating in search");
return ESP_ERR_NOT_FOUND;
} else {
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 < context->last_discrepancy) { // current id bit is before the last discrepancy bit
search_direction = (context->rom_number[rom_byte_index] & rom_bit_mask) ? 0x01 : 0x00; // follow previous way
} else {
search_direction = (rom_bit_index == context->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 serach direction
context->rom_number[rom_byte_index] |= rom_bit_mask;
} else {
context->rom_number[rom_byte_index] &= ~rom_bit_mask;
}
ESP_RETURN_ON_ERROR(onewire_bus_write_bit(context->bus_handle, search_direction),
TAG, "error while writing direction bit"); // set search direction
}
}
} else {
ESP_LOGD(TAG, "1-wire rom search finished");
return ESP_FAIL;
}
// if the search was successful
context->last_discrepancy = last_zero;
if (context->last_discrepancy == 0) { // last zero loops back to the first bit
context->last_device_flag = true;
}
if (onewire_check_crc8(context->rom_number, 7) != context->rom_number[7]) { // check crc
ESP_LOGE(TAG, "bad crc checksum of device with id " ONEWIRE_ROM_ID_STR, ONEWIRE_ROM_ID(context->rom_number));
return ESP_ERR_INVALID_CRC;
}
return ESP_OK;
}
esp_err_t onewire_rom_get_number(onewire_rom_search_context_handler_t context, uint8_t *rom_number_out)
{
ESP_RETURN_ON_FALSE(context, ESP_ERR_INVALID_ARG, TAG, "invalid context pointer");
ESP_RETURN_ON_FALSE(rom_number_out, ESP_ERR_INVALID_ARG, TAG, "invalid rom_number pointer");
memcpy(rom_number_out, context->rom_number, sizeof(context->rom_number));
return ESP_OK;
}

View File

@ -0,0 +1,77 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "onewire_bus_rmt.h"
#define ONEWIRE_CMD_SEARCH_ROM 0xF0
#define ONEWIRE_CMD_READ_ROM 0x33
#define ONEWIRE_CMD_MATCH_ROM 0x55
#define ONEWIRE_CMD_SKIP_ROM 0xCC
#define ONEWIRE_CMD_ALARM_SEARCH_ROM 0xEC
#define ONEWIRE_ROM_ID(id) (id)[0], (id)[1], (id)[2], (id)[3], (id)[4], (id)[5], (id)[6], (id)[7]
#define ONEWIRE_ROM_ID_STR "%02X%02X%02X%02X%02X%02X%02X%02X"
/**
* @brief Type of 1-wire ROM search algorithm context
*
*/
typedef struct onewire_rom_search_context_t *onewire_rom_search_context_handler_t;
/**
* @brief Calculate Dallas CRC value of given buffer
*
* @param[in] input Input buffer to calculate CRC value
* @param[in] input_size Size of input buffer
* @return CRC result of input buffer
*/
uint8_t onewire_check_crc8(uint8_t *input, size_t input_size);
/**
* @brief Create context for 1-wire ROM search algorithm
*
* @param[in] handle 1-wire handle used for ROM search
* @param[out] context_out Created context for ROM search algorithm
* @return
* - ESP_OK 1-wire ROM search context is created successfully.
* - ESP_ERR_INVALID_ARG Invalid argument.
*/
esp_err_t onewire_rom_search_context_create(onewire_bus_handle_t handle, onewire_rom_search_context_handler_t *context_out);
/**
* @brief Delete context for 1-wire ROM search algorithm
*
* @param[in] context Context for ROM search algorithm
* @return
* - ESP_OK 1-wire ROM search context is deleted successfully.
* - ESP_ERR_INVALID_ARG Invalid argument.
*/
esp_err_t onewire_rom_search_context_delete(onewire_rom_search_context_handler_t context);
/**
* @brief Search next device on 1-wire bus
*
* @param[in] context Context for ROM search algorithm
* @return
* - ESP_OK Successfully found a device
* - ESP_ERR_NOT_FOUND There are no device on the bus
* - ESP_ERR_INVALID_CRC Bad CRC value of found device
* - ESP_FAIL Reached last device on the bus, search algorighm finishes
*/
esp_err_t onewire_rom_search(onewire_rom_search_context_handler_t context);
/**
* @brief Get device ROM number from ROM search context
*
* @param[in] context Context for ROM search algorithm
* @param[out] rom_number_out Device ROM number
* @return
* - ESP_OK Get ROM numbuer from 1-wire ROM search context success.
* - ESP_ERR_INVALID_ARG Invalid argument.
*/
esp_err_t onewire_rom_get_number(onewire_rom_search_context_handler_t context, uint8_t *rom_number_out);

View File

@ -0,0 +1,438 @@
/*
* SPDX-FileCopyrightText: 2022 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 "esp_check.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "driver/rmt_types.h"
#include "driver/rmt_encoder.h"
#include "onewire_bus_rmt.h"
static const char *TAG = "onewire_rmt";
/**
* @brief RMT resolution for 1-wire bus, in Hz
*
*/
#define ONEWIRE_RMT_RESOLUTION_HZ 1000000
/**
* @brief 1-wire bus timing parameters, in us (1/ONEWIRE_RMT_RESOLUTION_HZ)
*
*/
#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_PRESENSE_WAIT_DURATION_MIN 15 // minimum duration for master to wait device to show its presence
#define ONEWIRE_RESET_PRESENSE_DURATION_MIN 60 // minimum duration for master to recognize device as present
#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
/*
Reset Pulse:
| RESET_PULSE | RESET_WAIT_DURATION |
| _DURATION | |
| | | | RESET | |
| | * | | _PRESENSE | |
| | | | _DURATION | |
------ -------
*: RESET_PRESENSE_WAIT_DURATION
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
*/
struct onewire_bus_t {
rmt_channel_handle_t tx_channel; /*!< rmt tx 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_channel_handle_t rx_channel; /*!< rmt rx channel handler */
rmt_symbol_word_t *rx_symbols; /*!< hold rmt raw symbols */
size_t max_rx_bytes; /*!< buffer size in byte for single receive transaction */
QueueHandle_t receive_queue;
};
const 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
};
const 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_symbol_word_t onewire_reset_pulse_symbol = {
.level0 = 0,
.duration0 = ONEWIRE_RESET_PULSE_DURATION,
.level1 = 1,
.duration1 = ONEWIRE_RESET_WAIT_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 bool onewire_rmt_rx_done_callback(rmt_channel_handle_t channel, rmt_rx_done_event_data_t *edata, void *user_data)
{
BaseType_t task_woken = pdFALSE;
struct onewire_bus_t *handle = (struct onewire_bus_t *)user_data;
xQueueSendFromISR(handle->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 | | Presense |
------
------
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 | | Presense |
------ ------
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)
{
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_PRESENSE_WAIT_DURATION_MIN &&
rmt_symbols[1].duration0 > ONEWIRE_RESET_PRESENSE_DURATION_MIN) {
return true;
}
} else { // bus is low before reset pulse(first pulse after rmt channel init)
if (rmt_symbols[0].duration0 > ONEWIRE_RESET_PRESENSE_WAIT_DURATION_MIN &&
rmt_symbols[1].duration1 > ONEWIRE_RESET_PRESENSE_DURATION_MIN) {
return true;
}
}
}
ESP_LOGE(TAG, "no device present on 1-wire bus");
return false;
}
static void onewire_rmt_decode_data(rmt_symbol_word_t *rmt_symbols, size_t symbol_num, uint8_t *decoded_bytes)
{
size_t byte_pos = 0, bit_pos = 0;
for (size_t i = 0; i < symbol_num; i ++) {
if (rmt_symbols[i].duration0 > ONEWIRE_SLOT_BIT_SAMPLE_TIME) { // 0 bit
decoded_bytes[byte_pos] &= ~(1 << bit_pos); // LSB first
} else { // 1 bit
decoded_bytes[byte_pos] |= 1 << bit_pos;
}
bit_pos ++;
if (bit_pos >= 8) {
bit_pos = 0;
byte_pos ++;
}
}
}
esp_err_t onewire_new_bus_rmt(onewire_rmt_config_t *config, onewire_bus_handle_t *handle_out)
{
ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "invalid config pointer");
ESP_RETURN_ON_FALSE(handle_out, ESP_ERR_INVALID_ARG, TAG, "invalid handle pointer");
esp_err_t ret = ESP_OK;
struct onewire_bus_t *handle = calloc(1, sizeof(struct onewire_bus_t));
ESP_GOTO_ON_FALSE(handle, ESP_ERR_NO_MEM, err, TAG, "memory allocation for 1-wire bus handler failed");
// 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, &handle->tx_bytes_encoder),
err, TAG, "create data tx 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, &handle->tx_copy_encoder),
err, TAG, "create reset pulse tx encoder failed");
// create rmt rx channel
rmt_rx_channel_config_t onewire_rx_channel_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.gpio_num = config->gpio_pin,
#if SOC_RMT_SUPPORT_RX_PINGPONG
.mem_block_symbols = 64, // when the chip is ping-pong capable, we can use less rx memory blocks
#else
.mem_block_symbols = config->max_rx_bytes * 8,
#endif
.resolution_hz = ONEWIRE_RMT_RESOLUTION_HZ, // in us
};
ESP_GOTO_ON_ERROR(rmt_new_rx_channel(&onewire_rx_channel_cfg, &handle->rx_channel),
err, TAG, "create rmt rx channel failed");
ESP_LOGI(TAG, "RMT Tx channel created for 1-wire bus");
// create rmt tx channel after rx channel
rmt_tx_channel_config_t onewire_tx_channel_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.gpio_num = config->gpio_pin,
.mem_block_symbols = 64, // ping-pong is always avaliable on tx channel, save hardware memory blocks
.resolution_hz = ONEWIRE_RMT_RESOLUTION_HZ, // in us
.trans_queue_depth = 4,
.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, &handle->tx_channel),
err, TAG, "create rmt tx channel failed");
ESP_LOGI(TAG, "RMT Rx channel created for 1-wire bus");
// allocate rmt rx symbol buffer
handle->rx_symbols = malloc(config->max_rx_bytes * sizeof(rmt_symbol_word_t) * 8);
ESP_GOTO_ON_FALSE(handle->rx_symbols, ESP_ERR_NO_MEM, err, TAG, "memory allocation for rx symbol buffer failed");
handle->max_rx_bytes = config->max_rx_bytes;
handle->receive_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t));
ESP_GOTO_ON_FALSE(handle->receive_queue, ESP_ERR_NO_MEM, err, TAG, "receive queue 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(handle->rx_channel, &cbs, handle),
err, TAG, "enable rmt rx channel failed");
// enable rmt channels
ESP_GOTO_ON_ERROR(rmt_enable(handle->rx_channel), err, TAG, "enable rmt rx channel failed");
ESP_GOTO_ON_ERROR(rmt_enable(handle->tx_channel), err, TAG, "enable rmt tx channel failed");
*handle_out = handle;
return ESP_OK;
err:
if (handle) {
onewire_del_bus(handle);
}
return ret;
}
esp_err_t onewire_del_bus(onewire_bus_handle_t handle)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
if (handle->tx_bytes_encoder) {
rmt_del_encoder(handle->tx_bytes_encoder);
}
if (handle->tx_copy_encoder) {
rmt_del_encoder(handle->tx_copy_encoder);
}
if (handle->rx_channel) {
rmt_disable(handle->rx_channel);
rmt_del_channel(handle->rx_channel);
}
if (handle->tx_channel) {
rmt_disable(handle->tx_channel);
rmt_del_channel(handle->tx_channel);
}
if(handle->receive_queue) {
vQueueDelete(handle->receive_queue);
}
if (handle->rx_symbols) {
free(handle->rx_symbols);
}
free(handle);
return ESP_OK;
}
esp_err_t onewire_bus_reset(onewire_bus_handle_t handle)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
// send reset pulse while receive presence pulse
ESP_RETURN_ON_ERROR(rmt_receive(handle->rx_channel, handle->rx_symbols, sizeof(rmt_symbol_word_t)*2, &onewire_rmt_rx_config),
TAG, "1-wire reset pulse receive failed");
ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_copy_encoder, &onewire_reset_pulse_symbol, sizeof(onewire_reset_pulse_symbol), &onewire_rmt_tx_config),
TAG, "1-wire reset pulse transmit failed");
// wait and check presence pulse
bool is_present = false;
rmt_rx_done_event_data_t rmt_rx_evt_data;
if (xQueueReceive(handle->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS) {
is_present = onewire_rmt_check_presence_pulse(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols);
}
return is_present ? ESP_OK : ESP_ERR_NOT_FOUND;
}
esp_err_t onewire_bus_write_bytes(onewire_bus_handle_t handle, const uint8_t *tx_data, uint8_t tx_data_size)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
ESP_RETURN_ON_FALSE(tx_data && tx_data_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid tx buffer or buffer size");
// transmit data
ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_bytes_encoder, tx_data, tx_data_size, &onewire_rmt_tx_config),
TAG, "1-wire data transmit failed");
// wait the transmission to complete
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(handle->tx_channel, 50), TAG, "wait for 1-wire data transmit failed");
return ESP_OK;
}
esp_err_t onewire_bus_read_bytes(onewire_bus_handle_t handle, uint8_t *rx_data, size_t rx_data_size)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
ESP_RETURN_ON_FALSE(rx_data && rx_data_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid rx buffer or buffer size");
ESP_RETURN_ON_FALSE(!(rx_data_size > handle->max_rx_bytes), ESP_ERR_INVALID_ARG,
TAG, "rx_data_size too large for buffer to hold");
uint8_t tx_buffer[rx_data_size];
memset(tx_buffer, 0xFF, rx_data_size); // transmit one bits to generate read clock
// transmit 1 bits while receiving
ESP_RETURN_ON_ERROR(rmt_receive(handle->rx_channel, handle->rx_symbols, rx_data_size * 8 * sizeof(rmt_symbol_word_t), &onewire_rmt_rx_config),
TAG, "1-wire data receive failed");
ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_bytes_encoder, tx_buffer, sizeof(tx_buffer), &onewire_rmt_tx_config),
TAG, "1-wire data transmit failed");
// wait the transmission finishes and decode data
rmt_rx_done_event_data_t rmt_rx_evt_data;
if (xQueueReceive(handle->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS) {
onewire_rmt_decode_data(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols, rx_data);
} else {
return ESP_ERR_TIMEOUT;
}
return ESP_OK;
}
esp_err_t onewire_bus_write_bit(onewire_bus_handle_t handle, uint8_t tx_bit)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
const rmt_symbol_word_t *symbol_to_transmit = tx_bit ? &onewire_bit1_symbol : &onewire_bit0_symbol;
// transmit bit
ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_copy_encoder, symbol_to_transmit, sizeof(onewire_bit1_symbol), &onewire_rmt_tx_config),
TAG, "1-wire bit transmit failed");
// wait the transmission to complete
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(handle->tx_channel, 50), TAG, "wait for 1-wire bit transmit failed");
return ESP_OK;
}
esp_err_t onewire_bus_read_bit(onewire_bus_handle_t handle, uint8_t *rx_bit)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
ESP_RETURN_ON_FALSE(rx_bit, ESP_ERR_INVALID_ARG, TAG, "invalid rx_bit pointer");
// transmit 1 bit while receiving
ESP_RETURN_ON_ERROR(rmt_receive(handle->rx_channel, handle->rx_symbols, sizeof(rmt_symbol_word_t), &onewire_rmt_rx_config),
TAG, "1-wire bit receive failed");
ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_copy_encoder, &onewire_bit1_symbol, sizeof(onewire_bit1_symbol), &onewire_rmt_tx_config),
TAG, "1-wire bit transmit failed");
// wait the transmission finishes and decode data
rmt_rx_done_event_data_t rmt_rx_evt_data;
if (xQueueReceive(handle->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS) {
uint8_t rx_buffer[1];
onewire_rmt_decode_data(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols, rx_buffer);
*rx_bit = rx_buffer[0] & 0x01;
} else {
return ESP_ERR_TIMEOUT;
}
return ESP_OK;
}

View File

@ -0,0 +1,106 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "driver/gpio.h"
/**
* @brief 1-wire bus config
*
*/
typedef struct {
gpio_num_t gpio_pin; /*!< gpio used for 1-wire bus */
uint8_t max_rx_bytes; /*!< should be larger than the largest possible single receive size */
} onewire_rmt_config_t;
/**
* @brief Type of 1-wire bus handle
*
*/
typedef struct onewire_bus_t *onewire_bus_handle_t;
/**
* @brief Install new 1-wire bus
*
* @param[in] config 1-wire bus configurations
* @param[out] handle_out Installed new 1-wire bus' handle
* @return
* - ESP_OK 1-wire bus is installed successfully.
* - ESP_ERR_INVALID_ARG Invalid argument.
* - ESP_ERR_NO_MEM Memory allocation failed.
*/
esp_err_t onewire_new_bus_rmt(onewire_rmt_config_t *config, onewire_bus_handle_t *handle_out);
/**
* @brief Delete existing 1-wire bus
*
* @param[in] handle 1-wire bus handle to be deleted
* @return
* - ESP_OK 1-wire bus is deleted successfully.
* - ESP_ERR_INVALID_ARG Invalid argument.
*/
esp_err_t onewire_del_bus(onewire_bus_handle_t handle);
/**
* @brief Send reset pulse on 1-wire bus, and detect if there are devices on the bus
*
* @param[in] handle 1-wire bus handle
* @return
* - ESP_OK There are devices present on 1-wire bus.
* - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
*/
esp_err_t onewire_bus_reset(onewire_bus_handle_t handle);
/**
* @brief Write bytes to 1-wire bus, this is a blocking function
*
* @param[in] handle 1-wire bus handle
* @param[in] tx_data pointer to data to be sent
* @param[in] tx_data_count number of data to be sent
* @return
* - ESP_OK Write bytes to 1-wire bus successfully.
* - ESP_ERR_INVALID_ARG Invalid argument.
*/
esp_err_t onewire_bus_write_bytes(onewire_bus_handle_t handle, const uint8_t *tx_data, uint8_t tx_data_size);
/**
* @brief Read bytes from 1-wire bus
*
* @note 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.
*
* @param[in] handle 1-wire bus handle
* @param[out] rx_data pointer to received data
* @param[in] rx_data_count number of received data
* @return
* - ESP_OK Read bytes from 1-wire bus successfully.
* - ESP_ERR_INVALID_ARG Invalid argument.
*/
esp_err_t onewire_bus_read_bytes(onewire_bus_handle_t handle, uint8_t *rx_data, size_t rx_data_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 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 onewire_bus_read_bit(onewire_bus_handle_t handle, uint8_t *rx_bit);

View File

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

View File

@ -0,0 +1,106 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <string.h>
#include "ds18b20.h"
#include "esp_check.h"
static const char *TAG = "ds18b20";
esp_err_t ds18b20_trigger_temperature_conversion(onewire_bus_handle_t handle, const uint8_t *rom_number)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
ESP_RETURN_ON_ERROR(onewire_bus_reset(handle), TAG, "error while resetting bus"); // reset bus and check if the device is present
uint8_t tx_buffer[10];
uint8_t tx_buffer_size;
if (rom_number) { // specify rom id
tx_buffer[0] = ONEWIRE_CMD_MATCH_ROM;
tx_buffer[9] = DS18B20_CMD_CONVERT_TEMP;
memcpy(&tx_buffer[1], rom_number, 8);
tx_buffer_size = 10;
} else { // skip rom id
tx_buffer[0] = ONEWIRE_CMD_SKIP_ROM;
tx_buffer[1] = DS18B20_CMD_CONVERT_TEMP;
tx_buffer_size = 2;
}
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, tx_buffer_size),
TAG, "error while triggering temperature convert");
return ESP_OK;
}
esp_err_t ds18b20_get_temperature(onewire_bus_handle_t handle, const uint8_t *rom_number, float *temperature)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
ESP_RETURN_ON_FALSE(temperature, ESP_ERR_INVALID_ARG, TAG, "invalid temperature pointer");
ESP_RETURN_ON_ERROR(onewire_bus_reset(handle), TAG, "error while resetting bus"); // reset bus and check if the device is present
ds18b20_scratchpad_t scratchpad;
uint8_t tx_buffer[10];
uint8_t tx_buffer_size;
if (rom_number) { // specify rom id
tx_buffer[0] = ONEWIRE_CMD_MATCH_ROM;
tx_buffer[9] = DS18B20_CMD_READ_SCRATCHPAD;
memcpy(&tx_buffer[1], rom_number, 8);
tx_buffer_size = 10;
} else {
tx_buffer[0] = ONEWIRE_CMD_SKIP_ROM;
tx_buffer[1] = DS18B20_CMD_READ_SCRATCHPAD;
tx_buffer_size = 2;
}
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, tx_buffer_size),
TAG, "error while sending read scratchpad command");
ESP_RETURN_ON_ERROR(onewire_bus_read_bytes(handle, (uint8_t *)&scratchpad, sizeof(scratchpad)),
TAG, "error while reading scratchpad command");
ESP_RETURN_ON_FALSE(onewire_check_crc8((uint8_t *)&scratchpad, 8) == scratchpad.crc_value, ESP_ERR_INVALID_CRC,
TAG, "crc error");
static const uint8_t lsb_mask[4] = { 0x07, 0x03, 0x01, 0x00 };
uint8_t lsb_masked = scratchpad.temp_lsb & (~lsb_mask[scratchpad.configuration >> 5]); // mask bits not used in low resolution
*temperature = (((int16_t)scratchpad.temp_msb << 8) | lsb_masked) / 16.0f;
return ESP_OK;
}
esp_err_t ds18b20_set_resolution(onewire_bus_handle_t handle, const uint8_t *rom_number, ds18b20_resolution_t resolution)
{
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
ESP_RETURN_ON_ERROR(onewire_bus_reset(handle), TAG, "error while resetting bus"); // reset bus and check if the device is present
uint8_t tx_buffer[10];
uint8_t tx_buffer_size;
if (rom_number) { // specify rom id
tx_buffer[0] = ONEWIRE_CMD_MATCH_ROM;
tx_buffer[9] = DS18B20_CMD_WRITE_SCRATCHPAD;
memcpy(&tx_buffer[1], rom_number, 8);
tx_buffer_size = 10;
} else {
tx_buffer[0] = ONEWIRE_CMD_SKIP_ROM;
tx_buffer[1] = DS18B20_CMD_WRITE_SCRATCHPAD;
tx_buffer_size = 2;
}
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, tx_buffer_size),
TAG, "error while sending read scratchpad command");
tx_buffer[0] = 0;
tx_buffer[1] = 0;
tx_buffer[2] = resolution;
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, 3),
TAG, "error while sending write scratchpad command");
return ESP_OK;
}

View File

@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#include "onewire_bus.h"
#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; /*!< configuration register */
uint8_t _reserved1;
uint8_t _reserved2;
uint8_t _reserved3;
uint8_t crc_value; /*!< crc value of scratchpad data */
} ds18b20_scratchpad_t;
/**
* @brief Enumeration of DS18B20's resolution config
*
*/
typedef enum {
DS18B20_RESOLUTION_12B = 0x7F, /*!< 750ms convert time */
DS18B20_RESOLUTION_11B = 0x5F, /*!< 375ms convert time */
DS18B20_RESOLUTION_10B = 0x3F, /*!< 187.5ms convert time */
DS18B20_RESOLUTION_9B = 0x1F, /*!< 93.75ms convert time */
} ds18b20_resolution_t;
/**
* @brief Trigger temperature conversion of DS18B20
*
* @param[in] handle 1-wire handle with DS18B20 on
* @param[in] rom_number ROM number to specify which DS18B20 to send command, NULL to skip ROM
* @return
* - ESP_OK Trigger tempreture convertsion success.
* - ESP_ERR_INVALID_ARG Invalid argument.
* - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
*/
esp_err_t ds18b20_trigger_temperature_conversion(onewire_bus_handle_t handle, const uint8_t *rom_number);
/**
* @brief Get temperature from DS18B20
*
* @param[in] handle 1-wire handle with DS18B20 on
* @param[in] rom_number ROM number to specify which DS18B20 to read from, NULL to skip ROM
* @param[out] temperature result from DS18B20
* @return
* - ESP_OK Get tempreture from DS18B20 success.
* - ESP_ERR_INVALID_ARG Invalid argument.
* - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
* - ESP_ERR_INVALID_CRC CRC check failed.
*/
esp_err_t ds18b20_get_temperature(onewire_bus_handle_t handle, const uint8_t *rom_number, float *temperature);
/**
* @brief Set DS18B20's temperation conversion resolution
*
* @param[in] handle 1-wire handle with DS18B20 on
* @param[in] rom_number ROM number to specify which DS18B20 to read from, NULL to skip ROM
* @param[in] resolution resolution of DS18B20's temperation conversion
* @return
* - ESP_OK Set DS18B20 resolution success.
* - ESP_ERR_INVALID_ARG Invalid argument.
* - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
*/
esp_err_t ds18b20_set_resolution(onewire_bus_handle_t handle, const uint8_t *rom_number, ds18b20_resolution_t resolution);

View File

@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2022 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_GPIO_PIN GPIO_NUM_5
#define EXAMPLE_ONEWIRE_MAX_DEVICES 5
void app_main(void)
{
onewire_rmt_config_t config = {
.gpio_pin = EXAMPLE_ONEWIRE_GPIO_PIN,
.max_rx_bytes = 10, // 10 tx bytes(1byte ROM command + 8byte ROM number + 1byte device command)
};
// install new 1-wire bus
onewire_bus_handle_t handle;
ESP_ERROR_CHECK(onewire_new_bus_rmt(&config, &handle));
ESP_LOGI(TAG, "1-wire bus installed");
// create 1-wire rom search context
onewire_rom_search_context_handler_t context_handler;
ESP_ERROR_CHECK(onewire_rom_search_context_create(handle, &context_handler));
uint8_t device_num = 0;
uint8_t device_rom_id[EXAMPLE_ONEWIRE_MAX_DEVICES][8];
// search for devices on the bus
do {
esp_err_t search_result = onewire_rom_search(context_handler);
if (search_result == ESP_ERR_INVALID_CRC) {
continue; // continue on crc error
} else if (search_result == ESP_FAIL || search_result == ESP_ERR_NOT_FOUND) {
break; // break on finish or no device
}
ESP_ERROR_CHECK(onewire_rom_get_number(context_handler, device_rom_id[device_num]));
ESP_LOGI(TAG, "found device with rom id " ONEWIRE_ROM_ID_STR, ONEWIRE_ROM_ID(device_rom_id[device_num]));
device_num ++;
} while (device_num < EXAMPLE_ONEWIRE_MAX_DEVICES);
// delete 1-wire rom search context
ESP_ERROR_CHECK(onewire_rom_search_context_delete(context_handler));
ESP_LOGI(TAG, "%d device%s found on 1-wire bus", device_num, device_num > 1 ? "s" : "");
// convert and read temperature
while (device_num > 0) {
esp_err_t err;
vTaskDelay(pdMS_TO_TICKS(200));
// set all sensors' temperature conversion resolution
err = ds18b20_set_resolution(handle, NULL, DS18B20_RESOLUTION_12B);
if (err != ESP_OK) {
continue;
}
// trigger all sensors to start temperature conversion
err = ds18b20_trigger_temperature_conversion(handle, NULL); // skip rom to send command to all devices on the bus
if (err != ESP_OK) {
continue;
}
vTaskDelay(pdMS_TO_TICKS(800)); // 12-bit resolution needs 750ms to convert
// get temperature from sensors
for (uint8_t i = 0; i < device_num; i ++) {
float temperature;
err = ds18b20_get_temperature(handle, device_rom_id[i], &temperature); // read scratchpad and get temperature
if (err != ESP_OK) {
continue;
}
ESP_LOGI(TAG, "temperature of device " ONEWIRE_ROM_ID_STR ": %.2fC", ONEWIRE_ROM_ID(device_rom_id[i]), temperature);
}
}
ESP_ERROR_CHECK(onewire_del_bus(handle));
ESP_LOGI(TAG, "1-wire bus deleted");
}

View File

@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2022 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.generic
def test_onewire_ds18b20_example(dut: Dut) -> None:
dut.expect_exact('onewire_rmt: RMT Tx channel created for 1-wire bus')
dut.expect_exact('onewire_rmt: RMT Rx channel created for 1-wire bus')
dut.expect_exact('example: 1-wire bus installed')
dut.expect_exact('example: 1-wire bus deleted')