Uart wait_tx_done function in ESP32 Micropython

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
Duality
Posts: 16
Joined: Tue Jun 09, 2020 2:43 pm

Uart wait_tx_done function in ESP32 Micropython

Post by Duality » Tue Oct 06, 2020 6:01 am

Hey guys,

As my previous posts suggest, I am implementing modbus on ESP32 with Micropython port. Sometimes the query from (esp32) master is not sent fully. It should be:

Code: Select all

b'\x01\x10\x00\x00\x00\x05\n\x00\xc8\x00\xc8\x00\xc8\x00\xc8\x00\xc8\x1eI'
However, on the slave I get this:

Code: Select all

b'\x01\x10\x00\x00\x00\x05\n\x00\xc8\x00\xc8\x00\xc8\x00'
This is because, RE and DE has been pulled down, while uart transmission. I want to know, exactly when the TX buffer is empty, so that I can pull the RE/DE pin down after that. Please suggest any way to do this. Someone else also raised a similar request on this forum.


Thanks in advance

Duality
Posts: 16
Joined: Tue Jun 09, 2020 2:43 pm

Re: Uart wait_tx_done function in ESP32 Micropython

Post by Duality » Wed Oct 07, 2020 12:34 pm

Hello All,

I tried to change the `machine_uart.c` by adding a function called machine_uart_waittxdone() as given below:

Code: Select all

STATIC mp_obj_t machine_uart_waittxdone(mp_obj_t self_in) {
	machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
	uart_wait_tx_done(self->uart_num, pdMS_TO_TICKS(0));
	return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_waittxdone_obj, machine_uart_waittxdone)
In the same file, I had add this line also:

Code: Select all

	{ MP_ROM_QSTR(MP_QSTR_waittxdone), MP_ROM_PTR(&machine_uart_waittxdone_obj) },
After building the micropython firmware, I was able to see the waittxdone() function in the machine.UART submodule. However, I see that the function might not be doing the work it is designed for. Can anyone suggest if I'm missing something here?


Regards

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Uart wait_tx_done function in ESP32 Micropython

Post by Roberthh » Wed Oct 07, 2020 4:08 pm

Did you try to increase the timeout to the worst value expected depending on message length & baud rate? That is the second parameter of wait_tx_done.

Duality
Posts: 16
Joined: Tue Jun 09, 2020 2:43 pm

Re: Uart wait_tx_done function in ESP32 Micropython

Post by Duality » Thu Oct 08, 2020 11:27 am

Hi Robert,

Thank you for your reply. Considering maximum number of registers to be written simultaneously are 50 (Modbus pdu length for writing this data 109 bytes), at 9600 baud, comes out to be 102 ms. I put 150 ms in the pdMS_TO_TICKS() as shown below and tried again. But I could not write even 25 holding registers at a time.

The modified function

Code: Select all

STATIC mp_obj_t machine_uart_waittxdone(mp_obj_t self_in) {
	machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
	uart_wait_tx_done(self->uart_num, pdMS_TO_TICKS(150));
	return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_waittxdone_obj, machine_uart_waittxdone);
My modbus micropython relevant function is as below (from uModbus library):

Code: Select all

    def _send(self, modbus_pdu, slave_addr):
        serial_pdu = bytearray()
        serial_pdu.append(slave_addr)
        serial_pdu.extend(modbus_pdu)
        crc = self._calculate_crc16(serial_pdu)
        serial_pdu.extend(crc)
        print('serial pdu: ',serial_pdu)    
        if self._ctrlPin:
            self._ctrlPin(1)
        self._uart.write(serial_pdu)
        
        time.sleep(0.1)
        if self._ctrlPin:
            self._uart.waittxdone()
            # while not self._uart.wait_tx_done():
            #     machine.idle()
            time.sleep_us(self._t35chars)
            self._ctrlPin(0)
When I connect the modbus slave to esp (esp as master). I get this communication on the slave side: (writing on 25 holding registers)

Code: Select all

Rx:000000-01 10 00 00 00 19 32 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 
Rx:000001-14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 D5 31 
Rx:000002-01 10 00 00 00 19 32 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 
Rx:000003-14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 D5 31 
Rx:000004-01 10 00 00 00 19 32 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 
Rx:000005-14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 00 14 D5 31 
I get success in writing multiple registers up to 14 registers. When I increase the time.sleep(0.1) sleep time, then I am able to write more registers. However, I need to make the wait_tx_done function work, so that I don't need to worry about this sleep time.

What am I missing to get this function work properly, so that it only returns when all the bytes are written as desired before ctrlPin is pulled low?

Thank you.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Uart wait_tx_done function in ESP32 Micropython

Post by Roberthh » Thu Oct 08, 2020 11:52 am

According to the specs, wait_tx_done() waits for the UARFT FIFO to be empty. But when you write in Python, the data goes to the tx_buffer first, and then into the UART fifo. So it may be, that you call wait_tx_done() before the UART FIFO has been filled. Could you try to insert a short sleep() between the uart.write call and the call for wait_rx_done()?

Duality
Posts: 16
Joined: Tue Jun 09, 2020 2:43 pm

Re: Uart wait_tx_done function in ESP32 Micropython

Post by Duality » Thu Oct 08, 2020 12:35 pm

I tried increasing the rx and tx buffer size from 256 to 512 in machine_uart.c. I was able to write multiple registers upto 25. But sometimes it was failing. I now, have the workaround. But if wait_tx_done() function works than the communication failures can be avoided.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Uart wait_tx_done function in ESP32 Micropython

Post by dhylands » Thu Oct 08, 2020 2:08 pm

I believe that the ESP32 UART has a HW FIFO and there would be a shift register and then most drivers also have a circular buffer. So the Tx isn’t done until the circular buffer is empty, and the FIFO is empty, and the shift register is empty.

I just wanted to make sure you’re checking all 3.

Duality
Posts: 16
Joined: Tue Jun 09, 2020 2:43 pm

Re: Uart wait_tx_done function in ESP32 Micropython

Post by Duality » Mon Oct 19, 2020 11:01 am

Hi Dave,
Thank you for your reply. micropython uart.write() function returns when the data is written to Tx ring buffer (whenever space is available). uart.wait_tx_done checks the Tx_FIFO (hardware FIFO). I have given enough sleep in between write and wait_tx_done functions. May be, I am missing the shift registers. I really don't know how to verify that. At this point the workaround is working, as I only have to write/read 4 registers of the Slave.

Regards

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Uart wait_tx_done function in ESP32 Micropython

Post by dhylands » Wed Oct 21, 2020 5:58 pm

I’m not familiar with the esp32 but normally there is a register bit which can be queries to see if the shift register is empty or not. It takes one character time for the shift register to empty and I know on the stm32 if I didn’t wait for this I would cut off the last character.

Duality
Posts: 16
Joined: Tue Jun 09, 2020 2:43 pm

Re: Uart wait_tx_done function in ESP32 Micropython

Post by Duality » Thu Oct 29, 2020 10:38 am

Thank you all for your inputs. Since I am working with the workaround for now, let's see if this works out well. I gave sleep time in between uart_wait_tx_done and for now it is working fine.

Regards

Post Reply