Serial communication with an ESP32-s3

sb_espressif
Posts: 28
Joined: Fri Dec 08, 2023 3:04 am

Serial communication with an ESP32-s3

Postby sb_espressif » Tue Sep 03, 2024 10:49 pm

Hi;

I have an esp32-s3 Feather development board, and have written a PID controller that I would like to tune. I imagine the way I'd like to do this is send PID coefficients to this esp32 as it's running, then be able to check the effects of my inputs.

I thought the way to do this was via serial communication, but after a day of trying and failing to get that working, I'd like to ask for advice as to how I might do this. I think ideally I could have a python script that I run, that prompts me for input, passes this to the esp, and allows me to see log output from the esp all within the same terminal. But I'm not picky, I need would like to be able to do this in a more efficient way than updating global variables, recompiling, reuploading, etc.

As an added bonus, I'm using a small VSCode extension called Teleplot (https://github.com/nesnes/teleplot) that lets me see realtime plots of data. It does this by reading the serial port output of the ESP (so, I can't have the ESP Monitor and Teleplot running at the same time, I've learned). Ideally I could provide input while being able to watch visual feedback about the effects of my tuning. But really any toehold to getting any of this to work would be great.

MicroController
Posts: 1734
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Serial communication with an ESP32-s3

Postby MicroController » Wed Sep 04, 2024 8:58 am

sb_espressif wrote:
Tue Sep 03, 2024 10:49 pm
I'm using a small VSCode extension called Teleplot (https://github.com/nesnes/teleplot) that lets me see realtime plots of data. It does this by reading the serial port output of the ESP (so, I can't have the ESP Monitor and Teleplot running at the same time, I've learned). Ideally I could provide input while being able to watch visual feedback about the effects of my tuning.
Note that the S3 has multiple UARTs, and a built-in USB-UART. You can e.g. use the USB-UART for flashing and idf-monitor'ing, and another UART (via external USB<->UART adapter) for data output.
You can also look into using the IDF's Console component to create an interactive serial 'terminal' to interact with your ESP.

Of course, you can also look into communicating with the ESP via WiFi (TCP), even via a simple ESP-hosted HTML web interface.

sb_espressif
Posts: 28
Joined: Fri Dec 08, 2023 3:04 am

Re: Serial communication with an ESP32-s3

Postby sb_espressif » Wed Sep 04, 2024 11:48 am

Hey thank you so much!
Note that the S3 has multiple UARTs, and a built-in USB-UART. You can e.g. use the USB-UART for flashing and idf-monitor'ing, and another UART (via external USB<->UART adapter) for data output.
I think this is the part where I'm getting confused. Is it possible to communicate bidirectionally via the USB-UART? Ideally I wouldn't have to go buy a USB-to-uart adaptor and set up a second connection to my laptop to do this.

What I have so far (which is pulled from the UART Echo example, and doesn't work for me):

Code: Select all

    /* Configure parameters of an UART driver,
     * communication pins and install the driver */
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };

    ESP_ERROR_CHECK(uart_driver_install(UART_NUM, UART_BUF_SIZE * 2, 0, 0, NULL, 0));
    ESP_ERROR_CHECK(uart_param_config(  UART_NUM, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(       UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));

    // Configure a temporary buffer for the incoming data
    uint8_t *data = (uint8_t *) malloc(UART_BUF_SIZE);

    while (1) {
        // Read data from the UART
        int len = uart_read_bytes(UART_NUM, data, (UART_BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
        // Write data back to the UART
        uart_write_bytes(UART_NUM, (const char *) data, len);
        if (len) {
            data[len] = '\0';
            ESP_LOGI(TAG, "Recv str: %s", (char *) data);
        } else {
            ESP_LOGI("UART", "No Data.");
        }
    }
Then, separately, I have a python script to try to send data like so:

Code: Select all

import serial
import threading
import time

ser = serial.Serial('/dev/tty.usbmodem324301', 115200, timeout=1) 

def send_pid_coefficients(p, i, d):
    #command = f'P={p},I={i},D={d}\n'
    command = 'test\n'
    ser.write(command.encode())
    print(f'Sent: {command}')

def read_from_serial():
    try:
        while True:
            if ser.in_waiting > 0:
                line = ser.readline().decode('utf-8').rstrip()
                print(f'Received: {line}')
    except serial.SerialException as e:
        print(f"Serial read error: {e}")
    except OSError as e:
        print(f"OS error: {e}")

# Start a thread to continuously read from the serial port
thread = threading.Thread(target=read_from_serial)
thread.daemon = True  # This makes sure the thread will exit when the main program does
thread.start()

# Example loop for sending data
try:
    while True:
        p = float(input("Enter P coefficient: "))
        i = float(input("Enter I coefficient: "))
        d = float(input("Enter D coefficient: "))
        send_pid_coefficients(p, i, d)
        time.sleep(1)
except KeyboardInterrupt:
    print("Exiting program...")
finally:
    if ser:
        ser.close()
        print("Serial port closed.")
When I run the above python, I can see it printing output from the esp ("No data"), but when it sends data ("test"), the esp never acknowledges receiving it. I've tried switching amongst UART_NUM_0, UART_NUM_1, and UART_NUM_2, and noodling around with the console settings in menuconfig (e.g. serial/JTAG, usb-cdc), but none of this changes the behavior. I'm sure I'm overlooking something silly but I'm not sure what it is (and, if it's not evident, it's my first time doing this).

Thanks!

MicroController
Posts: 1734
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Serial communication with an ESP32-s3

Postby MicroController » Wed Sep 04, 2024 6:04 pm

Is it possible to communicate bidirectionally via the USB-UART?
Yes, it is: https://docs.espressif.com/projects/esp ... tg-console
I think I have seen one or two magic functions which need to be called to set up/enable RX from the USB-UART.
Will try to dig up an example.

MicroController
Posts: 1734
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Serial communication with an ESP32-s3

Postby MicroController » Wed Sep 04, 2024 6:40 pm

https://github.com/espressif/esp-idf/issues/11948
has explanations by SpriteTM and some code.

sb_espressif
Posts: 28
Joined: Fri Dec 08, 2023 3:04 am

Re: Serial communication with an ESP32-s3

Postby sb_espressif » Fri Sep 06, 2024 2:20 pm

This works! Thank you so much! What intrepid internetting, I tried searching all manner of phrases to find help with this and never came across that git link you posted, very awesome.

At the risk of overstaying my welcome, I have a followup question. The above link furnishes some code that works fine but appears to be deprecated - when I compile it, I see warning e.g.

Code: Select all

esp_vfs_dev_usb_serial_jtag_set_tx_line_endings is deprecated. Please use usb_serial_jtag_vfs_set_tx_line_endings.
But, when When I switch to that, I get compile errors saying that function is not found.

Digging around inside the esp-idf rep (I'm using 5.3 as of this writing), I can see this function lives in this - is "component" the correct word?:
https://github.com/espressif/esp-idf/tr ... erial_jtag

I admit components confuse me. I *think* that - because this component lives within the idf installation - I can include it by adding this to my CMakeLists.txt:

Code: Select all

REQUIRES esp_driver_usb_serial_jtag
However, when I do this, I suddenly get different errors:

Code: Select all

fatal error: esp_timer.h: No such file or directory
   99 | #include "esp_timer.h"
I can modify my CMakeLists.txt to:

Code: Select all

REQUIRES esp_timer esp_driver_usb_serial_jtag
which resolves the above, but then I start getting cascading errors for every other include in my program down the line. All of this makes me suspect I am doing something wrong.

Can you help me understand how I can bring the example you cite up to current standards?

MicroController
Posts: 1734
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Serial communication with an ESP32-s3

Postby MicroController » Fri Sep 06, 2024 8:14 pm

Your approach looks exactly right, declaring every component your application ("main component") directly uses/requires in its CMakeLists.txt.

Normally, there are a bunch of IDF components included implicitly. I imagine that these implicit default dependencies may get overridden as soon as you declare at least one dependency, like esp_driver_usb_serial_jtag, explicitly. But I don't know that and to what extent this is true.

However, it is normal and, IMHO, good practice to declare all (direct) dependencies explicitly, irrespective of whether or not a few of them may already be drawn in by default or transitively.

Who is online

Users browsing this forum: atx823, Majestic-12 [Bot] and 221 guests