Serial communication with an ESP32-s3
-
- Posts: 28
- Joined: Fri Dec 08, 2023 3:04 am
Serial communication with an ESP32-s3
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.
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.
-
- Posts: 1725
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: Serial communication with an ESP32-s3
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.sb_espressif wrote: ↑Tue Sep 03, 2024 10:49 pmI'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.
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.
-
- Posts: 28
- Joined: Fri Dec 08, 2023 3:04 am
Re: Serial communication with an ESP32-s3
Hey thank you so much!
What I have so far (which is pulled from the UART Echo example, and doesn't work for me):
Then, separately, I have a python script to try to send data like so:
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!
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.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.
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.");
}
}
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.")
Thanks!
-
- Posts: 1725
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: Serial communication with an ESP32-s3
Yes, it is: https://docs.espressif.com/projects/esp ... tg-consoleIs it possible to communicate bidirectionally via the USB-UART?
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.
-
- Posts: 1725
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: Serial communication with an ESP32-s3
https://github.com/espressif/esp-idf/issues/11948
has explanations by SpriteTM and some code.
has explanations by SpriteTM and some code.
-
- Posts: 28
- Joined: Fri Dec 08, 2023 3:04 am
Re: Serial communication with an ESP32-s3
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.
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:
However, when I do this, I suddenly get different errors:
I can modify my CMakeLists.txt to:
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?
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.
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
Code: Select all
fatal error: esp_timer.h: No such file or directory
99 | #include "esp_timer.h"
Code: Select all
REQUIRES esp_timer esp_driver_usb_serial_jtag
Can you help me understand how I can bring the example you cite up to current standards?
-
- Posts: 1725
- Joined: Mon Oct 17, 2022 7:38 pm
- Location: Europe, Germany
Re: Serial communication with an ESP32-s3
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.
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: Google [Bot] and 307 guests