1

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-)

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

11
  • Hi, thanks for your comment. But I don't know how to get CLK as the output of serial converter doesn't have it. A just edit the post with the python code as requested Commented Apr 1 at 10:19
  • 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. Commented Apr 1 at 10:31
  • @Cincinnatus, my mistake for the "rgb ['red', 'green', 'blue']", it's copy/paste issue. The command is now edited and send the correct value. I add the option to readback single character for the echo but it's not used in this case. I only used the check_echo=True (check after the whole frame is sent). Note that, with my code, I was able to send 200 command without communication issue, and one time up to 17000 commands without comm issues. Commented Apr 1 at 10:52
  • @Cincinnatus: There is no CLK in UART communications - the "A" of "UART" stands for "asynchronous", which in this case basically means "without a clock signal". Commented Apr 1 at 10:56
  • @psmears you are right, sorry, too much used to I2C communications Commented Apr 1 at 10:57

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.