Skip to content

Conversation

Kainarx
Copy link
Collaborator

@Kainarx Kainarx commented May 15, 2025

Checklist

  • Component contains License
  • Component contains README.md
  • Component contains idf_component.yml file with url field defined
  • Component was added to upload job
  • Component was added to build job
  • Optional: Component contains unit tests
  • CI passing

Change description

Closes espressif/esp-idf#10128

@suda-morris suda-morris requested a review from Copilot May 15, 2025 02:37
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds a new software-emulated UART component using the ESP32 RMT peripheral, including encoder, decoder, device API, and an example.

  • Introduces rmt_uart_encoder for framing bytes into RMT symbols.
  • Implements rmt_uart device API for transmit, receive, decode, and lifecycle management.
  • Provides public headers, CMake component registration, example application, and README.

Reviewed Changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/rmt_uart_encoder.h Header for RMT UART encoder creation API
src/rmt_uart_encoder.c Implementation of UART-to-RMT encoder logic
src/rmt_uart.c Core RMT UART device implementation (tx, rx, decode)
include/rmt_uart.h Public configuration and API declarations
include/rmt_uart_type.h Type definitions and callback signatures
example/main/soft_uart_main.c Sample application showing transmit/receive usage
example/main/idf_component.yml & CMake Example component registration
example/README.md Documentation and usage instructions
CMakeLists.txt Component build registration
Comments suppressed due to low confidence (2)

rmt_soft_uart/src/rmt_uart.c:4

  • License identifier differs from other files (Apache-2.0). Ensure consistent licensing across the component.
* SPDX-License-Identifier: Unlicense OR CC0-1.0

rmt_soft_uart/src/rmt_uart_encoder.c:24

  • Critical encoding logic is introduced here but lacks accompanying unit tests to verify framing and state transitions.
static size_t rmt_encode_uart(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)

@Kainarx Kainarx force-pushed the feat/add_soft_uart_component branch from e4257c1 to 8bc6033 Compare May 15, 2025 10:15
@Kainarx Kainarx force-pushed the feat/add_soft_uart_component branch from 8bc6033 to c0bbf11 Compare May 15, 2025 10:27
@suda-morris suda-morris requested a review from Copilot May 16, 2025 08:17
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a new soft UART component that uses the RMT peripheral for UART signal emulation. Key changes include the implementation of UART encoding/decoding in new source and header files, integration with ESP-IDF component configuration and CI workflows, and an example demonstrating usage of the component.

Reviewed Changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated no comments.

File Description
soft_uart/src/rmt_uart_encoder.[ch] New encoder implementation with state-machine for UART framing.
soft_uart/src/rmt_uart.c Core driver implementation for transmitting/receiving via RMT.
soft_uart/include/*.h Public API and type definitions for the soft UART component.
Various example and config files Example application, component manifest, and CI integration.
Comments suppressed due to low confidence (1)

soft_uart/src/rmt_uart_encoder.c:103

  • The 'state' field of the allocated encoder is not explicitly initialized. To ensure predictable behavior of the state machine in rmt_encode_uart(), initialize uart_encoder->state to RMT_ENCODING_RESET right after allocation.
rmt_uart_encoder_t *uart_encoder = rmt_alloc_encoder_mem(sizeof(rmt_uart_encoder_t));

@Kainarx Kainarx force-pushed the feat/add_soft_uart_component branch from c0bbf11 to b2ee24b Compare May 26, 2025 07:58
@suda-morris suda-morris force-pushed the feat/add_soft_uart_component branch from b2ee24b to 33e8a0e Compare July 8, 2025 15:18
@Kainarx Kainarx force-pushed the feat/add_soft_uart_component branch from 33e8a0e to 0b76636 Compare July 10, 2025 07:23
// [N + 1 + (1 or 2)] Stop bit (always 1)

// extract the data byte
uint8_t data_byte = (decode_context->raw_data >> 1) & ((1 << data_bits) - 1);

Check warning

Code scanning / clang-tidy

Left shift by '254' overflows the capacity of 'int' [clang-analyzer-core.BitwiseShift] Warning

Left shift by '254' overflows the capacity of 'int' [clang-analyzer-core.BitwiseShift]
// [N + 1 + (1 or 2)] Stop bit (always 1)

// extract the data byte
uint8_t data_byte = (decode_context->raw_data >> 1) & ((1 << data_bits) - 1);

Check warning

Code scanning / clang-tidy

Left shift by '255' overflows the capacity of 'int' [clang-analyzer-core.BitwiseShift] Warning

Left shift by '255' overflows the capacity of 'int' [clang-analyzer-core.BitwiseShift]

// do memory copy, the pingpong buffer should not be very large, so the memory copy should be fast
if (edata->flags.is_last) {
memcpy(rx_context->rx_symbols_buf + *read_symbol_index, edata->received_symbols, edata->num_symbols * sizeof(rmt_symbol_word_t));

Check warning

Code scanning / clang-tidy

Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memcpy_s' in case of C11 [clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling] Warning

Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memcpy_s' in case of C11 [clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling]
}
*read_symbol_index = 0;
} else {
memcpy(rx_context->rx_symbols_buf + *read_symbol_index, edata->received_symbols, edata->num_symbols * sizeof(rmt_symbol_word_t));

Check warning

Code scanning / clang-tidy

Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memcpy_s' in case of C11 [clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling] Warning

Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard. Replace with analogous functions that support length arguments or provides boundary checks such as 'memcpy_s' in case of C11 [clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling]
@suda-morris suda-morris force-pushed the feat/add_soft_uart_component branch from 0b76636 to fe76581 Compare July 14, 2025 06:18
#include <stddef.h>
#include "soc/soc_caps.h"
#include "driver/rmt_types.h"
#include "sdkconfig.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not used

#include <stdbool.h>
#include <stdlib.h>
#include <stddef.h>
#include "soc/soc_caps.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not used by this header file

if(CONFIG_SOC_RMT_SUPPORTED)
list(APPEND srcs "src/uart_emu_rmt.c" "src/uart_emu_rmt_encoder.c")
endif()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include($ENV{IDF_PATH}/tools/cmake/version.cmake)


[![Component Registry](https://components.espressif.com/components/espressif/uart_emu/badge.svg)](https://components.espressif.com/components/espressif/uart_emu)

This directory contains an implementation for software UART. Currently only RMT is supported as the software UART backend.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's hard if it's a hardware UART or software UART.. because RMT is still a hardware.

Suggested change
This directory contains an implementation for software UART. Currently only RMT is supported as the software UART backend.
This directory contains an implementation of UART driver emulated in various ways. Currently only RMT is supported as the backend.


# Programming Guide

- [UART EMU](index.md)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- [UART EMU](index.md)
- [UART Emulator](index.md)

@@ -0,0 +1,8 @@
[book]
title = "UART EMU"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
title = "UART EMU"
title = "UART Emulator Driver"

@@ -0,0 +1,71 @@
# UART EMU
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# UART EMU
# UART Emulator Programming Guide

@@ -0,0 +1,71 @@
# UART EMU

## Allocate UART EMULATOR Object with RMT Backend
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Allocate UART EMULATOR Object with RMT Backend
## Allocate UART Object with RMT Backend

## API Documentation

For detailed API documentation, please refer to the header files:
- [uart_emu.h](./include/uart_emu.h) - Main public API
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the Markdown file is compiled into html and hosted on the Github pages, user can't find this link.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Luckily this error is also caught by the CI.

.rx_io_num = UART_EMU_RX_PIN,
.baud_rate = UART_EMU_BAUD_RATE,
.data_bits = UART_EMU_DATA_8_BITS,
.stop_bits = UART_EMU_STOP_BITS_1,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I the a naming inconsistent in the UART_EMU_DATA_8_BITS and UART_EMU_STOP_BITS_1

uart_emu_rmt_config_t rmt_config = {
.tx_trans_queue_depth = UART_EMU_RMT_TX_TRANS_QUEUE_DEPTH,
.tx_mem_block_symbols = UART_EMU_RMT_MEM_BLOCK_SYMBOLS,
.rx_mem_block_symbols = UART_EMU_RMT_MEM_BLOCK_SYMBOLS,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is part of the programming guide, please add some comments beside the code, explaning how user can set these values

version: "1.0.0"
description: Driver for UART emulator
url: https://github.com/espressif/idf-extra-components/tree/master/uart_emu
issues: "https://github.com/espressif/idf-extra-components/issues"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing repository and documentation

#define UART_EMU_RMT_MEM_BLOCK_SYMBOLS 1024
#else
#define UART_EMU_RMT_MEM_BLOCK_SYMBOLS SOC_RMT_MEM_WORDS_PER_CHANNEL
#endif
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to expose the SOC_RMT_MEM_WORDS_PER_CHANNEL and SOC_RMT_SUPPORT_DMA to the user's project. Refer to how led_strip handles it

// Set to 1 to use DMA for driving the LED strip, 0 otherwise
// Please note the RMT DMA feature is only available on chips e.g. ESP32-S3/P4
#define LED_STRIP_USE_DMA  0

#if LED_STRIP_USE_DMA
// Numbers of the LED in the strip
#define LED_STRIP_LED_COUNT 256
#define LED_STRIP_MEMORY_BLOCK_WORDS 1024 // this determines the DMA block size
#else
// Numbers of the LED in the strip
#define LED_STRIP_LED_COUNT 24
#define LED_STRIP_MEMORY_BLOCK_WORDS 0 // let the driver choose a proper memory block size automatically
#endif // LED_STRIP_USE_DMA

uart_emu_event_tx_callbacks_t uart_tx_cbs = {
.on_tx_trans_done = uart_emu_rmt_tx_event_cbs,
};
ESP_ERROR_CHECK(uart_emu_register_tx_event_callbacks(uart_device, &uart_tx_cbs, xTaskGetCurrentTaskHandle()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add some comment on why we passed in a task handle?

// create a task to read data from the UART
xTaskCreate(uart_read_task, "uTask", 4096, uart_device, 4, NULL);

char sendbuf[] = "RMT UART, transmission! RMT UART, transmission! RMT UART, transmission! RMT UART, transmission! RMT UART, transmission! RMT UART, transmission! RMT UART, transmission!";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question, can we use char *sendbuf = "xxx"; to reduce the use of the stack? I don't know if the transmit function only support a buffer from a stack?


int count = 0;
while (1) {
if (!ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(50))) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the transmit is fast, after you call uart_emu_transmit for 16 times, you can only get notified by 15 times or fewer

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this example, is the trans done event callback necessary? How can we make it more practical?

while (1) {
/* Read from the UART */
ESP_ERROR_CHECK(uart_emu_receive(uart_device, (uint8_t *)receive_symbols, UART_EMU_RMT_RX_PINGPONG_BUFFER_SIZE * sizeof(rmt_symbol_word_t)));
if (xQueueReceive(receive_queue, &rx_done_event_data, portMAX_DELAY)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how can we receive a long package? the uart_emu_rmt_rx_event_cbs will be called multiple times. How many times do you want the user to call the uart_emu_receive?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can refer to the twai onchip driver, offer a "rx_from_isr" function 🤔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Using RMT as UART (IDFGH-8682)
2 participants