mirror of
https://github.com/espressif/esp-idf.git
synced 2025-11-02 13:45:46 +00:00
console: fix a bug preventing us from starting a CLI on non-default UART
It is now possible to start a REPL CLI on another UART than the default one. Closes https://github.com/espressif/esp-idf/issues/6897
This commit is contained in:
6
examples/peripherals/uart/uart_repl/CMakeLists.txt
Normal file
6
examples/peripherals/uart/uart_repl/CMakeLists.txt
Normal 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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(uart_repl)
|
||||
8
examples/peripherals/uart/uart_repl/Makefile
Normal file
8
examples/peripherals/uart/uart_repl/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := uart_repl
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
61
examples/peripherals/uart/uart_repl/README.md
Normal file
61
examples/peripherals/uart/uart_repl/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# UART REPL Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates how to use REPL console on a different UART than the default one.
|
||||
It also shows how to connect these two UART together, either for testing or for sending commands
|
||||
without any human interaction.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
The example can be run on any ESP board that have at least 2 UARTs. The development board shall be connected to a
|
||||
PC with a single USB cable for flashing and monitoring. If you are willing to monitor the console UART, you may use
|
||||
a 3.3V compatible USB-to-Serial dongle on its GPIO pin.
|
||||
|
||||
### Setup the Hardware
|
||||
|
||||
No external connection is needed in order to run the example. However, as stated before, if you are willing to see what
|
||||
is going on on the second UART (console UART), you can connect pins CONSOLE_UART_TX_PIN (5 by default) and
|
||||
CONSOLE_UART_RX_PIN (4 by default) to a Serial-to-USB adapter.
|
||||
|
||||
### Configure the project
|
||||
|
||||
The default values, located at the top of `main/uart_repl_example_main.c` can be changed such as:
|
||||
DEFAULT_UART_CHANNEL, CONSOLE_UART_CHANNEL, DEFAULT_UART_RX_PIN, DEFAULT_UART_TX_PIN, CONSOLE_UART_RX_PIN,
|
||||
CONSOLE_UART_TX_PIN, UARTS_BAUD_RATE, TASK_STACK_SIZE, and READ_BUF_SIZE.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view default UART's serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
The example will set up the default UART to use DEFAULT_UART_RX_PIN and DEFAULT_UART_TX_PIN. Then, it will set up
|
||||
the REPL console on the second UART. Finally, it will connect both UARTs together in order to let default UART
|
||||
be able to send commands and receive replies to and from the console UART.
|
||||
|
||||
Here is a diagram of what UARTs will look like:
|
||||
|
||||
```
|
||||
UART default UART console
|
||||
|
||||
USB monitoring <------ TX -----------> RX----+
|
||||
v
|
||||
Parse command
|
||||
and output result
|
||||
| Optional 3.3V
|
||||
RX <----------- TX<---+ (----------->) Serial-to-USB
|
||||
Adapter
|
||||
```
|
||||
|
||||
If everything goes fine, the output on default UART should be "Result: Success". Else, it should be "Result: Failure".
|
||||
2
examples/peripherals/uart/uart_repl/main/CMakeLists.txt
Normal file
2
examples/peripherals/uart/uart_repl/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "uart_repl_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
3
examples/peripherals/uart/uart_repl/main/component.mk
Normal file
3
examples/peripherals/uart/uart_repl/main/component.mk
Normal file
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -0,0 +1,181 @@
|
||||
/* UART Echo Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/uart.h"
|
||||
#include "soc/uart_periph.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_console.h"
|
||||
#include "linenoise/linenoise.h"
|
||||
#include <string.h>
|
||||
|
||||
#define DEFAULT_UART_CHANNEL (0)
|
||||
#define CONSOLE_UART_CHANNEL (1 - DEFAULT_UART_CHANNEL)
|
||||
#define DEFAULT_UART_RX_PIN (3)
|
||||
#define DEFAULT_UART_TX_PIN (2)
|
||||
#define CONSOLE_UART_RX_PIN (4)
|
||||
#define CONSOLE_UART_TX_PIN (5)
|
||||
|
||||
#define UARTS_BAUD_RATE (115200)
|
||||
#define TASK_STACK_SIZE (2048)
|
||||
#define READ_BUF_SIZE (1024)
|
||||
|
||||
/* Message printed by the "consoletest" command.
|
||||
* It will also be used by the default UART to check the reply of the second
|
||||
* UART. As end of line characters are not standard here (\n, \r\n, \r...),
|
||||
* let's not include it in this string. */
|
||||
const char test_message[] = "This is an example string, if you can read this, the example is a success!";
|
||||
|
||||
/**
|
||||
* @brief This function connects default UART TX to console UART RX and default
|
||||
* UART RX to console UART TX. The purpose is to send commands to the console
|
||||
* and get the reply directly by reading RX FIFO.
|
||||
*/
|
||||
static void connect_uarts(void)
|
||||
{
|
||||
esp_rom_gpio_connect_out_signal(DEFAULT_UART_RX_PIN, uart_periph_signal[1].tx_sig, false, false);
|
||||
esp_rom_gpio_connect_in_signal(DEFAULT_UART_RX_PIN, uart_periph_signal[0].rx_sig, false);
|
||||
|
||||
esp_rom_gpio_connect_out_signal(DEFAULT_UART_TX_PIN, uart_periph_signal[0].tx_sig, false, false);
|
||||
esp_rom_gpio_connect_in_signal(DEFAULT_UART_TX_PIN, uart_periph_signal[1].rx_sig, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnect default UART from the console UART, this is used once
|
||||
* testing is finished, it will let us print messages on the UART without
|
||||
* sending them back to the console UART. Else, we would get an infinite
|
||||
* loop.
|
||||
*/
|
||||
static void disconnect_uarts(void)
|
||||
{
|
||||
esp_rom_gpio_connect_out_signal(CONSOLE_UART_TX_PIN, uart_periph_signal[1].tx_sig, false, false);
|
||||
esp_rom_gpio_connect_in_signal(CONSOLE_UART_RX_PIN, uart_periph_signal[1].rx_sig, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure and install the default UART, then, connect it to the
|
||||
* console UART.
|
||||
*/
|
||||
static void configure_uarts(void)
|
||||
{
|
||||
/* Configure parameters of an UART driver,
|
||||
* communication pins and install the driver */
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = UARTS_BAUD_RATE,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = UART_SCLK_APB,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(uart_driver_install(DEFAULT_UART_CHANNEL, READ_BUF_SIZE * 2, 0, 0, NULL, 0));
|
||||
ESP_ERROR_CHECK(uart_param_config(DEFAULT_UART_CHANNEL, &uart_config));
|
||||
ESP_ERROR_CHECK(uart_set_pin(DEFAULT_UART_CHANNEL, DEFAULT_UART_TX_PIN, DEFAULT_UART_RX_PIN,
|
||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
||||
|
||||
|
||||
connect_uarts();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function called when command `consoletest` will be invoked.
|
||||
* It will simply print `test_message` defined above.
|
||||
*/
|
||||
static int console_test(int argc, char **argv) {
|
||||
printf("%s\n", test_message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function executed in another task then main one (as the one main
|
||||
* executes REPL console).
|
||||
* It will send "consoletest" command to the console UART and then read back
|
||||
* the response on RX.
|
||||
* The response shall contain the test_message string.
|
||||
*/
|
||||
static void send_commands(void* arg) {
|
||||
static char data[READ_BUF_SIZE];
|
||||
char command[] = "consoletest\n";
|
||||
int len = 0;
|
||||
void* substring = NULL;
|
||||
|
||||
/* Discard the first messages sent by the console. */
|
||||
do {
|
||||
len = uart_read_bytes(DEFAULT_UART_CHANNEL, data, READ_BUF_SIZE, 100 / portTICK_RATE_MS);
|
||||
} while (len == 0);
|
||||
|
||||
if ( len == -1 ) {
|
||||
goto end;
|
||||
}
|
||||
/* Send the command `consoletest` to the console UART. */
|
||||
len = uart_write_bytes(DEFAULT_UART_CHANNEL, command, sizeof(command));
|
||||
if ( len == -1 ) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Get the answer back from the console, give it some delay. */
|
||||
do {
|
||||
len = uart_read_bytes(DEFAULT_UART_CHANNEL, data, READ_BUF_SIZE - 1, 250 / portTICK_RATE_MS);
|
||||
} while (len == 0);
|
||||
|
||||
if ( len == -1 ) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we can find test_message in the received message. Before
|
||||
* that, we need to add a NULL character to terminate the string.
|
||||
*/
|
||||
data[len] = 0;
|
||||
substring = strcasestr(data, test_message);
|
||||
|
||||
end:
|
||||
/* This is a must to not send anything to the console anymore! */
|
||||
disconnect_uarts();
|
||||
printf("Result: %s\n", substring == NULL ? "Failure" : "Success");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_console_repl_t *repl = NULL;
|
||||
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
|
||||
repl_config.prompt = "repl >";
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "consoletest",
|
||||
.help = "Test console by sending a message",
|
||||
.func = &console_test,
|
||||
};
|
||||
esp_console_dev_uart_config_t uart_config = {
|
||||
.channel = CONSOLE_UART_CHANNEL,
|
||||
.baud_rate = UARTS_BAUD_RATE,
|
||||
.tx_gpio_num = CONSOLE_UART_TX_PIN,
|
||||
.rx_gpio_num = CONSOLE_UART_RX_PIN,
|
||||
};
|
||||
/**
|
||||
* As we don't have a real serial terminal, (we just use default UART to
|
||||
* send and receive commands, ) we won't handle any escape sequence, so the
|
||||
* easiest thing to do is set the console to "dumb" mode. */
|
||||
linenoiseSetDumbMode(1);
|
||||
|
||||
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
|
||||
configure_uarts();
|
||||
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
|
||||
/* Create a task for sending and receiving commands to and from the second UART. */
|
||||
xTaskCreate(send_commands, "send_commands_task", TASK_STACK_SIZE, NULL, 10, NULL);
|
||||
|
||||
ESP_ERROR_CHECK(esp_console_start_repl(repl));
|
||||
}
|
||||
Reference in New Issue
Block a user