REPL conflicting with UART(0)

Discussion about programs, libraries and tools that work with MicroPython. Mostly these are provided by a third party.
Target audience: All users and developers of MicroPython.
Post Reply
User avatar
bandaangosta
Posts: 6
Joined: Thu May 18, 2017 1:39 pm
Location: Santiago, Chile
Contact:

REPL conflicting with UART(0)

Post by bandaangosta » Wed Mar 27, 2019 7:51 pm

I am developing a mobile robot prototype having a master device (Raspberry Pi) and one (or more) slaves (ESP8266-based board: Wemos D1 Mini, Mini Lite or Mini Pro). The master handles high level services including a web server, video streaming, audio reproduction and so on. Slave(s) handle low-level PWM driving of DC motors (wheels, grip, etc.), sensors reading, LED lights control, etc. Master and slaves use half-duplex serial communication (UART). As the ESP8266 boards I am using have onboard microUSB connector and FTDI chip, I am just using UART(0) and standard serial libraries on both sides. No need for extra wiring for this purpose. I have done the same in the past with Arduino Nano boards. In this project I will be replacing the Arduinos with ESP8266 boards and so make use of (the incredible) Micropython instead of Arduino C.

I got it working, although there is something I don't understand. From the documentation and some posts in this forum (viewtopic.php?t=1740), REPL is "only run when nothing else is running", meaning only after main.py exits. I don't know if I am misunderstanding that statement or something has changed since I read that sentence (or my code is just wrong somewhere), because I can only the communications as expected when I disable REPL.

On the master:

Code: Select all

# Master device (host) main program

import serial
import time

slave = serial.Serial('/dev/ttyUSB0', 115200, timeout=3)
try:
    timer = time.time()

    while True:
        if slave.in_waiting > 0:
            msgFromSlave = slave.readline()            
            print('{:.3f}: [uart read] {}'.format(time.time(), msgFromSlave.decode('utf-8').strip('\r\n')))
     
        timeCheck = time.time()
        if timeCheck - timer > 3:
            timer = timeCheck            
            print('{:.3f}: [print] Asking slave 2+2?'.format(time.time()))
            slave.write(b'2 + 2?\r\n')
        
        # Uncomment following line to reduce host CPU usage
        # time.sleep(.1) 

except KeyboardInterrupt:
    print(' Ctrl+C was pressed')

finally:
    if slave.is_open:
        slave.close()
        print(' Serial connection was closed') 
On the ESP8266 board (firmware esp8266-20190125-v1.10.bin):

Code: Select all

# Slave device (ESP8266 board) main program
import machine
import time

led = machine.Pin(2, machine.Pin.OUT) # set to 2 for builtin LED in Wemos D1 mini pro

def main():
    uart = machine.UART(0)                         # init with given baudrate
    uart.init(115200, timeout=3000) # init with given parameters

    ticker = time.ticks_ms()

    while True:
        if uart.any():
            hostMsg = uart.readline()
            if hostMsg is not None:
                strMsg = str(hostMsg.decode('utf-8'))
                uart.write(strMsg.strip('\r\n') + ' 2 plus 2 equals 4' + '\r\n')
        
        timeCheck = time.ticks_ms()
        if time.ticks_diff(timeCheck, ticker) > 1000:
            ticker = timeCheck
            uart.write('Slave time: ' + str(timeCheck) + '\r\n')
            led.value(not led.value()) # Toggle LED            

# Run main loop
main()
On the host, when running python testSerial.py the console output is:

Code: Select all

$ python host/testSerial.py 
1553711688.217: [uart read] Slave time: 3272
1553711689.217: [uart read] Slave time: 4273
1553711690.219: [uart read] Slave time: 5274
1553711691.187: [print] Asking slave 2+2?
1553711691.220: [uart read] Slave time: 6275
1553711692.221: [uart read] Slave time: 7276
1553711693.221: [uart read] Slave time: 8277
1553711694.187: [print] Asking slave 2+2?
1553711694.223: [uart read] Slave time: 9278
1553711695.224: [uart read] Slave time: 10279
1553711696.225: [uart read] Slave time: 11280
1553711697.187: [print] Asking slave 2+2?
1553711697.226: [uart read] Slave time: 12281
^C Ctrl+C was pressed
 Serial connection was closed
As seen above, slave->master communications works, but not the other way around (no response from slave).
However, if I disable REPL in UART(0) in boot.py, bidirectional communication is established as expected:
boot.py:

Code: Select all

# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
import uos, machine
uos.dupterm(None, 1) # disable REPL on UART(0)
import gc
#import webrepl
#webrepl.start()
gc.collect()

Code: Select all

$ python host/testSerial.py 
1553711500.240: [uart read] Slave time: 3277
1553711501.241: [uart read] Slave time: 4278
1553711502.242: [uart read] Slave time: 5279
1553711502.840: [print] Asking slave 2+2?
1553711502.853: [uart read] 2 + 2? 2 plus 2 equals 4
1553711503.243: [uart read] Slave time: 6280
1553711504.244: [uart read] Slave time: 7281
1553711505.245: [uart read] Slave time: 8282
1553711505.840: [print] Asking slave 2+2?
1553711505.852: [uart read] 2 + 2? 2 plus 2 equals 4
1553711506.245: [uart read] Slave time: 9283
1553711507.247: [uart read] Slave time: 10284
1553711508.248: [uart read] Slave time: 11285
1553711508.840: [print] Asking slave 2+2?
1553711508.853: [uart read] 2 + 2? 2 plus 2 equals 4
1553711509.249: [uart read] Slave time: 12286
^C Ctrl+C was pressed
 Serial connection was closed
Is this correct and expected behavior?

On the other hand, when disabling REPL on UART(0) as above, code loading with ampy does not work anymore. Only way I could find to continue testing is to enable webREPL and upload the files through the websocket. I guess ampy uses the (raw) REPL connection.

Can you please enlighten me with an explanation of how REPL is implemented?

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

Re: REPL conflicting with UART(0)

Post by dhylands » Wed Mar 27, 2019 8:38 pm

This thread explains it:
https://github.com/micropython/micropython/issues/4225

So yes, uart(0) is assigned to the REPL by default, in which case you can use sys.stdin or sys.stdout. If you want to use uart(0) then you need to unassign it from the REPL.

User avatar
bandaangosta
Posts: 6
Joined: Thu May 18, 2017 1:39 pm
Location: Santiago, Chile
Contact:

Re: REPL conflicting with UART(0)

Post by bandaangosta » Thu Mar 28, 2019 2:42 am

dhylands wrote:
Wed Mar 27, 2019 8:38 pm
This thread explains it:
https://github.com/micropython/micropython/issues/4225

So yes, uart(0) is assigned to the REPL by default, in which case you can use sys.stdin or sys.stdout. If you want to use uart(0) then you need to unassign it from the REPL.
Thanks for that quick reply, dhylands. It is a feature of the implementation, then.

Regarding ampy failing to work after unassigning UART(0), are you aware of any way I can continue uploading the code via UART(0), i.e., the USB connection to the PC? I know there are other applications like ampy available, but haven't tried any yet. WebREPL works, but its inefficient and I was hoping to disable Wifi connectivity.

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

Re: REPL conflicting with UART(0)

Post by dhylands » Thu Mar 28, 2019 3:39 am

I've written rshell which you can find here: https://github.com/dhylands/rshell/

It connects over the serial interface and allows files to be transferred to/from the board as well as acting like a terminal program to allow you get a REPL.

User avatar
bandaangosta
Posts: 6
Joined: Thu May 18, 2017 1:39 pm
Location: Santiago, Chile
Contact:

Re: REPL conflicting with UART(0)

Post by bandaangosta » Thu Mar 28, 2019 4:48 am

I will give rshell a try, thanks!

Alternatively, following a short snippet in https://github.com/micropython/micropython/issues/4225, I rewrote my example code to only disable REPL during the master-slave communication and re-enable it on exit or if a "special" message is received:

Code: Select all

# Slave device (ESP8266 board) main program

import machine
import uos

def main():
    try:    
        uos.dupterm(None, 1) # disable REPL on UART(0)
    
        uart = machine.UART(0)                 
        uart.init(115200, timeout=3000) 

        while True:
            if uart.any():
                hostMsg = uart.readline()
                if hostMsg is not None:                    
                    strMsg = hostMsg.decode().strip('\r\n')
                    if strMsg == '\x00':
                        raise Exception
                    else:                        
                        uart.write('I received: ' + strMsg + '\r\n')                                    
    except Exception:
        uart.write('Exception was raised')
    finally:
        uos.dupterm(machine.UART(0, 115200), 1)

# Run main loop
main()
To restore REPL (allowing ampy to function again), I run the following code on the host PC:

Code: Select all

# Master device (host) main program

import serial

with serial.Serial('/dev/ttyUSB0', 115200, timeout=3) as slave:
    print('Sending "restore REPL" command...')
    slave.write('\x00\r\n'.encode())

rink
Posts: 1
Joined: Mon Apr 01, 2019 1:21 pm

REPL Duplication with dupterm ESP32

Post by rink » Mon Apr 01, 2019 1:36 pm

I'm trying to duplicate the REPL to UART2
uart2=machine.UART(2, tx=17,rx=16,baudrate=9600)
uos.dupterm(uart2)

The result is that whatever is typed through the serial connection on USB/UART(0) is duplicated in uart2.
But not the other way around.
When typing in uart2, no data appears on UART(0).

However, the data is retained in the UART(0) buffer and can be displayed by using uart2.readline() and thus does not trigger the REPL editor.

Is there a solution to this?

Thanks so much!

Jibun no kage
Posts: 144
Joined: Mon Jul 25, 2022 9:45 pm

Re: REPL conflicting with UART(0)

Post by Jibun no kage » Thu Aug 11, 2022 7:57 pm

Was this ever resolved? Just interested in if there was a solution?

Post Reply