Page 1 of 2

Why UART2 read do not work after hard reset?

Posted: Fri Jan 15, 2021 5:02 pm
by Divergentti
I am reading MH-Z19 CO2 sensor from UART1 (pins 17 and 18) asynchronously, code here: https://github.com/divergentti/airquali ... /MHZ19B.py

and in the main.py code https://github.com/divergentti/airquali ... en/main.py I start reading loop in the main:

Code: Select all

async def main():
    # asyncio.create_task(mqtt_up_loop()) """
    # Create loops here!
    loop = asyncio.get_event_loop()
    loop.create_task(co2sensor.read_co2_loop())
    loop.create_task(wifinet.collect_carbage_and_update_status_loop())
    loop.create_task(show_what_i_do())

    if (display.connect_to_wifi is True) and (wifinet.network_connected is False):
        await wifinet.connect_to_network()

    if wifinet.network_connected is True:
        display.row_by_row_text("Network up, begin operations", 'blue')
        await display.run_display()
    else:
        display.row_by_row_text("Network down, running setup", 'yellow')
        await display.show_network_setup_screen()

    # loop.run_forever()

    while True:

        await asyncio.sleep(1)


if __name__ == "__main__":
    asyncio.run(main())
Everything works fine if ESP32 is soft booted, like just after uploading code to the ESP32, but if ESP32 is power on booted without console connection to the USB serial, everything else but CO2 reading works. It starts working if I connect to ESP32 serial USB with Putty, when ESP32 seems to reboot again.

Two questions: why ESP32 boots again if I connect to it with USB serial and why UART1 (pins 16 and 17) is not functioning after cold boot? They are somehow related.

EDIT: today (January 16th) I added PMS7003 particle sensor reading asynchronously via StreamReader to the code and used UART2 in GPIO pins 32 and 33 and that seems to work fine after power on boot etc. UART0 is not usable due to REPL, and I tested UART1 in other GPIO pins, but that brings error during boot. Code including asynchronous read for PMS7003 sensor at https://github.com/divergentti/airquali ... en/main.py

Re: Why UART2 read do not work after hard reset?

Posted: Sun Jan 17, 2021 1:20 pm
by Divergentti
Update:

If boot reason is 1 (power on boot), UART2 does not work. I was able to fix it by:

# CO2 sensor
co2sensor = MHZ19bCO2(uart=CO2_SENSOR_UART, rxpin=CO2_SENSOR_RX_PIN, txpin=CO2_SENSOR_TX_PIN)

if reset_cause() == 1:
del co2sensor
utime.sleep(5)
co2sensor = MHZ19bCO2(uart=CO2_SENSOR_UART, rxpin=CO2_SENSOR_RX_PIN, txpin=CO2_SENSOR_TX_PIN)

and this works so that in the beginning I get values 65535 and then correct values. I also checked that WebREPL do not mess UART2 initialization, but for some reason after power on boot returned bytearray is like bytearray(b'\x00\x00\xa2\xff\x86\x01\x9a=\x00') but when UART works it is like bytearray(b'\xff\x86\x01\xfd=\x00\x00\x00?') where first FF is the key for proper reading.

I have no idea why first UART2 initialization fails after power on boot but works soft boot, but turnaround seems to be deleting sensor object and recreating it again.

Re: Why UART2 read do not work after hard reset?

Posted: Sun Jan 17, 2021 1:58 pm
by Roberthh
If you still use Pins 16 and 17: on some boards these are used for flash or SPIRAM. Normally the board crashes when you try to use them for other purposes. In your case obviously not. But there may still be a side effect. You could try to use other pins for the UART.

Re: Why UART2 read do not work after hard reset?

Posted: Sun Jan 17, 2021 2:06 pm
by Divergentti
Roberthh wrote:
Sun Jan 17, 2021 1:58 pm
If you still use Pins 16 and 17: on some boards these are used for flash or SPIRAM. Normally the board crashes when you try to use them for other purposes. In your case obviously not. But there may still be a side effect. You could try to use other pins for the UART.
I tried UART2 with pins 34 & 35, but I got an error during bootup. By documentation UART0 and UART1 could be muxed to other pins, but UART2 can not be muxed. UART0 is reserved for the REPL, UART1 in my case is pins 32 & 33.

Other pins are in use as follows:

# Do not use UART0 (pins 3, 4) if you need REPL!
CO2_SENSOR_UART = 2
CO2_SENSOR_TX_PIN = 17
CO2_SENSOR_RX_PIN = 16
PARTICLE_SENSOR_UART = 1
PARTICLE_SENSOR_TX = 32
PARTICLE_SENSOR_RX = 33
# https://docs.espressif.com/projects/esp ... aster.html
TFT_SPI = 1 # HSPI = ID1
TFT_DC_PIN = 4
TFT_CS_PIN = 15
TFT_MOSI_PIN = 13 # SDI
TFT_CLK_PIN = 14 # SCK
TFT_RST_PIN = 2
TFT_MISO_PIN = 12
TOUCHSCREEN_SPI = 2 # VSPI = ID2
TFT_TOUCH_SCLK_PIN = 18
TFT_TOUCH_MOSI_PIN = 23 # T_DIN
TFT_TOUCH_MISO_PIN = 19 # T_DO
TFT_TOUCH_CS_PIN = 5
TFT_TOUCH_IRQ_PIN = 0
I2C_SDA_PIN = 21
I2C_SCL_PIN = 22

Re: Why UART2 read do not work after hard reset?

Posted: Sun Jan 17, 2021 2:42 pm
by Roberthh
Pins 34 - 39 are input only. You can use them for RX but not for TX.

Re: Why UART2 read do not work after hard reset?

Posted: Sun Jan 17, 2021 3:30 pm
by Divergentti
Roberthh wrote:
Sun Jan 17, 2021 2:42 pm
Pins 34 - 39 are input only. You can use them for RX but not for TX.
So true. I already forgot that. Thank you for heads-up.

I re-checked documentation of ESP32 and by the doc UART2 shall also be moved to other pins too https://www.espressif.com/sites/default ... eet_en.pdf but in this discussion someone referred that UART2 can not be moved to other pins https://github.com/espressif/arduino-es ... -325694606

--- Update:

I tested UART2 on 25 & 27. It behaves excactly same as with pins 16&17.

Soft reboot when UART2 in pins 25 & 27:

(REPL console)

PMS dictionary: {'PM2_5': 6, 'PM1_0_ATM': 4, 'PM10_0_ATM': 7, 'VERSION': 151, 'ERROR': 2, 'CHECKSUM': 599, 'PM1_0': 4, 'PM10_0': 7, 'PM2_5_ATM': 6, 'PCNT_0_5': 151, 'PCNT_5_0': 1, 'FRAME_LENGTH': 28, 'PCNT_2_5': 4, 'PCNT_1_0': 40, 'PCNT_10_0': 0, 'PCNT_0_3': 810}
Air quality index: 36.06867
CO2: 410
Average: 410.0

= works ok, as it does with 16 & 17 when REPL is connected or soft boot, but no values if power on boot, like if I connect USB to power supply only.

if delete co2sensor object after reset_cause 1 and re-create, CO2 reading works ok.

It is not about the delay in case of boot reason 1, sensor object really needs deletion and re-creation.

I also tested original driver class https://github.com/dr-mod/co2-monitorin ... /mhz19b.py, which is not asynchronous, and behavior is the same. I have no idea why.

Re: Why UART2 read do not work after hard reset?

Posted: Tue Jan 19, 2021 10:12 am
by Roberthh
Looking into the code you linked, I would do the init of the UARTs differently. so e.g. instead of:

Code: Select all

        self.sensor = UART(uart)
        self.sensor.init(baudrate=9600, bits=8, parity=None, stop=1, rx=rxpin, tx=txpin)
I recommend to use a single statement:

Code: Select all

        self.sensor = UART(uart, baudrate=9600, bits=8, parity=None, stop=1, rx=rxpin, tx=txpin)
The difference: In the first call the UART would be initiliazed with the defaults, and then these values are changed. But still the default Pins (9,10 and 16, 17) would have been changed to a specific setting in the first call.

Re: Why UART2 read do not work after hard reset?

Posted: Tue Jan 19, 2021 5:47 pm
by Divergentti
Roberthh wrote:
Tue Jan 19, 2021 10:12 am
Looking into the code you linked, I would do the init of the UARTs differently. so e.g. instead of:

Code: Select all

        self.sensor = UART(uart)
        self.sensor.init(baudrate=9600, bits=8, parity=None, stop=1, rx=rxpin, tx=txpin)
I recommend to use a single statement:

Code: Select all

        self.sensor = UART(uart, baudrate=9600, bits=8, parity=None, stop=1, rx=rxpin, tx=txpin)
The difference: In the first call the UART would be initiliazed with the defaults, and then these values are changed. But still the default Pins (9,10 and 16, 17) would have been changed to a specific setting in the first call.
I changed MHZ19_AS.py https://github.com/divergentti/airquali ... Z19B_AS.py
to self.sensor = UART(uart, baudrate=9600, bits=8, parity=None, stop=1, rx=rxpin, tx=txpin) and tried main.py without re-creation of the object after bootcause 1, but that did not fix it. Problem remains.

I changed UART init to same to PMS7003_AS.py as well https://github.com/divergentti/airquali ... 7003_as.py

Code main.py at https://github.com/divergentti/airquali ... en/main.py

Re: Why UART2 read do not work after hard reset?

Posted: Tue Jan 19, 2021 7:15 pm
by marcidy
Divergentti wrote:
Sun Jan 17, 2021 1:20 pm
for some reason after power on boot returned bytearray is like bytearray(b'\x00\x00\xa2\xff\x86\x01\x9a=\x00') but when UART works it is like bytearray(b'\xff\x86\x01\xfd=\x00\x00\x00?') where first FF is the key for proper reading.

Code: Select all

b'\x00\x00\xa2\xff\x86\x01\x9a=\x00'
b'\xff\x86\x01\xfd=\x00\x00\x00
That looks like noise, \xff\x86\x01 is there in both cases. Leading \x00 can be that the connected device isn't powered up yet, pulling down the lines and triggering a phantom transfer.

Do you have external pull-ups? Can you (electrically) enable the sensors after initializing the UART? This probably not a code / micropython issue but an electrical issue. If you can't do anything else, you can read bytes in a loop till you get \xff on initialization.

It's good to have sensible hardware defaults via pull-ups, and control the start-up and initialization of connected devices whenever possible. If you have access to the pins, you might want to add some external pull-ups to RX and TX.

Re: Why UART2 read do not work after hard reset?

Posted: Tue Jan 19, 2021 9:38 pm
by Divergentti
marcidy wrote:
Tue Jan 19, 2021 7:15 pm
Divergentti wrote:
Sun Jan 17, 2021 1:20 pm
for some reason after power on boot returned bytearray is like bytearray(b'\x00\x00\xa2\xff\x86\x01\x9a=\x00') but when UART works it is like bytearray(b'\xff\x86\x01\xfd=\x00\x00\x00?') where first FF is the key for proper reading.

Code: Select all

b'\x00\x00\xa2\xff\x86\x01\x9a=\x00'
b'\xff\x86\x01\xfd=\x00\x00\x00
That looks like noise, \xff\x86\x01 is there in both cases. Leading \x00 can be that the connected device isn't powered up yet, pulling down the lines and triggering a phantom transfer.

Do you have external pull-ups? Can you (electrically) enable the sensors after initializing the UART? This probably not a code / micropython issue but an electrical issue. If you can't do anything else, you can read bytes in a loop till you get \xff on initialization.

It's good to have sensible hardware defaults via pull-ups, and control the start-up and initialization of connected devices whenever possible. If you have access to the pins, you might want to add some external pull-ups to RX and TX.
Thank you for heads up.

It indeed looks like a noise and perhaps I shall add error frame counter to the MHZ19_AS.py class. If checksum from UART bytearray is wrong, counter increases. Noise is possible, because chip is SMD chip in the programming board and I use dupont connectors, which are bad. I am not using pullups, because I understood ESP32 has pullups as default and if I would like to use open drain GPIO, I have to turn them off? At least I have done so for circuits which are "grounding" through GPIO pin.

But then the but: this problem can be repeated without any changes to the hardware (= noise theory do not apply). With deletion of the sensor object everything works robust without errors (tested over night), but if I do not delete the sensor object as in the code I do, nothing works after power on boot, but works fine with soft boot.