WebRepl input blocked by loop with utime.sleep()?

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
mkreis
Posts: 2
Joined: Thu Dec 19, 2019 3:23 pm

WebRepl input blocked by loop with utime.sleep()?

Post by mkreis » Thu Dec 19, 2019 3:35 pm

Dear all,

I recently started with MicroPython on an ESP8266 and am a bit stuck right now.

My goal is to have the WebRepl running in background to easily update my code, while a worker loop is running in main.py.
(The worker loop is reading an analog value and conditionally posting to MQTT -- but I removed this functionality for debugging reasons, so this an be ignored for now.)

The problem I have is that although I can connect to the WebRepl, I can not type in anything, but uploading files is working fine.
It is definitely not a browser issue because I tried multiple browsers and computers. Only when removing the "while True" loop, the Repl works as expected.
Now I tried to use asyncio Task or machine.lightsleep() -- nothing changed.

Can you please help me out how I can run a loop in parallel to WebRepl? Thanks a lot!

main.py to reproduce on ESP8266 with MicroPython 1.11:

Code: Select all

import machine
from utime import sleep
import webrepl

webrepl.start(password='1234')

while True:
  #tried without success: machine.lightsleep(1000)
  sleep(1)

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

Re: WebRepl input blocked by loop with utime.sleep()?

Post by Roberthh » Thu Dec 19, 2019 5:22 pm

REPL as well as WebRepl is only active when there is not code executed in the main thread. You can start you background task in a separate thread and then finish the script which started it. But there may be still resource conflicts.

cgglzpy
Posts: 47
Joined: Thu Jul 18, 2019 4:20 pm

Re: WebRepl input blocked by loop with utime.sleep()?

Post by cgglzpy » Thu Dec 19, 2019 7:06 pm

Hi!
If the "loop is reading an analog value and conditionally posting to MQTT" runs for example every 5 or 10 seconds
you can do this "worker loop" with callback and a timer interrupt, something like:

Code: Select all

from machine import Timer
tim = Timer(-1)

###
# Your adc, MQTT Client definitions here
###
my_value = 0
def post_to_mqtt_callback(x, adc=my_adc,  mqtt_client=my_mqtt_client, value=my_value):
	value = adc.read()
	if value > 10:
		mqtt_client.publish(#your message)
	else:
		pass
		
# Now you initialize the timer

tim.init(period=10000, mode=Timer.PERIODIC, callback=post_to_mqtt_callback)

# Now every ten seconds the timer will interrupt any running code an execute the function callback

So if there is no while loop running in your code, the WebREPL should be accessible without problem.
You may also want to add some print statements to see if the callback is running properly or add a try/except block to print exceptions.

jedie
Posts: 252
Joined: Fri Jan 29, 2016 12:32 pm
Contact:

Re: WebRepl input blocked by loop with utime.sleep()?

Post by jedie » Thu Dec 19, 2019 7:06 pm

mkreis wrote:
Thu Dec 19, 2019 3:35 pm
My goal is to have the WebRepl running in background to easily update my code, while a worker loop is running in main.py.
I think on the ESP8266 the resources are too limited.

My solution is to switch between OTA update mode and normal operation mode after each boot in main.py, see: https://github.com/jedie/micropython-so ... rc/main.py

So in each mode all resources are available.

I also implement a reset via web interface. So I can do an OTA update without having to go to the device ;)

mkreis
Posts: 2
Joined: Thu Dec 19, 2019 3:23 pm

Re: WebRepl input blocked by loop with utime.sleep()?

Post by mkreis » Fri Dec 20, 2019 8:59 am

Hi all,

thanks a lot for the quick and very useful feedback!
Using a timer interrupt I was able to achieve my goal. It is not as sophisticated as the Jedie's OTA, but good enough for now.

User avatar
tiny
Posts: 6
Joined: Tue Dec 31, 2019 1:26 am

Re: WebRepl input blocked by loop with utime.sleep()?

Post by tiny » Wed Jan 01, 2020 3:24 pm

I'm just beginning to explore usage of _thread in micropython, actually coming out of reading this thread. Took a bit of sleuthing to create a 'kill pill' structure that worked, so I thought I'd share it here.

#full-on working example of a background thread:

Code: Select all

from time import sleep
import _thread
def do_ping(x):
      global kill_threads
      while kill_threads != True:
          print("thread still alive....")
          sleep(3)

global kill_threads
kill_threads = False
x=0
_thread.start_new_thread(do_ping,[x])
#when ready to kill it, simple as
kill_threads = True
I dont fully understand why an arg is _required_ when you create the thread. I also dont understand why I couldnt use the 'x' var as the kill-pill (ie, use it as the 'while not true' setter). I do know this worked and I was able to run a background script that didnt pause use of REPL whatsoever when it slept.

http://docs.micropython.org/en/latest/l ... le-_thread (which will just say nothing and direct you to...)
https://docs.python.org/3.5/library/_th ... le-_thread


Cheers fellow micropython enthusiasts :)

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: WebRepl input blocked by loop with utime.sleep()?

Post by pythoncoder » Thu Jan 02, 2020 2:21 pm

tiny wrote:
Wed Jan 01, 2020 3:24 pm
...
I dont fully understand why an arg is _required_ when you create the thread.
As I understand it the arg could be an empty tuple (). Its purpose is to enable a thread to be initialised with an arbitrary number of positional args.
I also dont understand why I couldnt use the 'x' var as the kill-pill (ie, use it as the 'while not true' setter).
The reason why x couldn't be used is the way Python does argument passing. Integers are passed by value, not by reference. So a function gets a copy of the value of x: when you change x outside of the function, the copy within the function does not change. This cuts both ways: if a function changes x the external value does not change:

Code: Select all

x = 0
def foo(x):
    x += 1
If you run foo(x) you will find that the x available at the REPL is unchanged. Note that structures like lists and dictionaries are passed by reference. So you can kill without a global like this

Code: Select all

from time import sleep
import _thread
def do_ping(kill):
      while not kill[0]:
          print("thread still alive....")
          sleep(3)

kill = [False]
_thread.start_new_thread(do_ping, (kill,))  # Tuple contains one arg, a list. This is passed by reference.
#when ready to kill it, simple as
kill[0] = True  # You are changing the contents of a list element which is seen by the function.
Lastly the "global" in

Code: Select all

global kill_threads
is redundant. You only need to specify global in the body of the function here:

Code: Select all

def do_ping(x):
      global kill_threads
Peter Hinch
Index to my micropython libraries.

User avatar
tiny
Posts: 6
Joined: Tue Dec 31, 2019 1:26 am

Re: WebRepl input blocked by loop with utime.sleep()?

Post by tiny » Thu Jan 02, 2020 4:36 pm

Wow, very cool pythoncoder/peterhinch! Excellent explanation. Honored you took time to respond. Have perused your github with relish :) Vars pass value, structures pass reference. This and many other intricacies. I enjoy them all. Thanks again for your input. 8-)

Post Reply