I'm using pyserial with most of my project and I often have communication issue.
Software Setup:
- Windows 10
- Python 3.11.9
- Pyserial 3.5 (installed with pip on venv)
- port settings:
self.port = serial.Serial(self.com_port, baudrate=115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE)
Hardware Setup:
- USB/Serial converter used: ESP-Programmer (from ESPRESSIF which uses FT2232HL chip from FTDI)
- Shunt RX and TX
Test:
- Send UART message on TX and read it back on RX. Comparing both, some characters are missing
- i.e:
TX: rgb green
RX: rgb gree
The 'n' is missing in the RX => Communication error (occurs ≃ every 200 iteration in the while loop -code below-)
- i.e:
Using an oscilloscope, I captured the failing frame: Framing error for characters 'n'
And as comparison, the same frame without issue: Frame without issue captured by scope
Following captures, timing are not equivalent for last low state on 'n' (0x6E) characters and seems to result on issue on the stop bit (this issue can occurs on different character of a message)
What can cause this issue ? Can it be related to my used of pyserial ? Does someone already had this behavior ?
Test with other converter:
- I also tried with USB/serial converter TTL-RS232-3V3 from FTDI and got the same kind of issue when sending complete sentence with port.write(<complete_sentence>) while I got very few issue when sending sentence character by character (following the code below).
- Following the pyserial documentation, sending entire sentence is not prohibited. What can cause this difference using this USB/serial converter.
EDIT 01: Please find below the code used to send and readback data:
def try_send_command(self, command, error_echo_single_char):
self.flush_input()
if type(command) is str:
print(f"TX: {command}")
command = command + "\n\r"
# Write command letter by letter to avoid comm issue with ESP32 (and ESP Prog)
for _ in command:
self.port.write(_.encode())
if error_echo_single_char:
# Check if single character is correctly sent
readback_char = self.port.read().decode()
print(f"Readback single char: {readback_char}")
if not _ == readback_char:
print(f"error while redback char: TX:{_} RX:{readback_char}")
raise ESP32CommTest.ESP32CommTestException("Echo issue with single char. Command not proccessed !")
elif type(command) is bytes:
print(f"TX: {command}")
self.port.write(command)
def send_command(self, command, check_echo=True, timeout=None, try_nb=None, end_flags=[], error_flag=[], error_echo_single_char=False):
while try_nb > 0:
try:
self.try_send_command(command=command, error_echo_single_char=error_echo_single_char)
if check_echo:
lines = self.read_lines(command, timeout=timeout, end_flags=end_flags, error_flags=error_flag)
else:
lines = self.read_lines(timeout=timeout, end_flags=end_flags, error_flags=error_flag)
if self.config.get("uart_esp32_retry_on_fail", True) and len(error_flag) > 0:
for l in lines:
if any([f in l for f in error_flag]):
raise ESP32CommTest.ESP32CommTestException("Test communication error !")
return lines
except ESP32CommTest.ESP32CommTestException as ex:
print(str(ex))
if error_echo_single_char:
raise ex
raise ex
try_nb -= 1
time.sleep(self.config.get("delay_before_retry_send_command", 2))
#End while
raise ESP32CommTest.ESP32CommTestException("Test communication command failed !")
And here is the loop to send UART data:
def set_rgb(self, color:str=Literal["red", "green", "blue", "white", "off"], timeout:int=None) -> list:
"""
Change LED color in order to perform measure for each segment using Feasa module
:param color: Select the color to be set (or clear with "off")
:return: List of output frames receives
"""
return self.send_command(command=f"rgb {color}", timeout=timeout, end_flags=["OK"], error_flag=["UNKNOWN_COLOR", "UNKNOWN_COMMAND"], check_echo=True, error_echo_single_char=False)
def send_cmd_until_fail(self):
color = ["red", "green", "blue"]
i = 0
c = 0
while True:
print(f"Command n°: {i}")
self.set_rgb(color[c], timeout=2)
time.sleep(0.2)
c += 1
if c > 2:
c = 0
i += 1
If anyone already has this behavior, or if you have some advises to avoid this problems it would be great.
Best regards, Antoine
f"rgb {color}"gives as a result"rgb ['red', 'green', 'blue']". This doesn't correspond to the command we see in the plots. I would suggest you create a simple code where you send a command and then read what is replied back. Don't read single characters one at a time. Send a command, wait a bit of time and then read the whole input buffer.